DD4hep  1.30.0
Detector Description Toolkit for High Energy Physics
PluginServiceV2.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * (c) Copyright 2013 CERN *
3 * *
4 * This software is distributed under the terms of the GNU General Public *
5 * Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". *
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 <boost/algorithm/string.hpp>
18 
19 #include <dirent.h>
20 #include <dlfcn.h>
21 
22 #include <cstdlib>
23 #include <fstream>
24 #include <iostream>
25 #include <memory>
26 #include <regex>
27 #include <vector>
28 
29 #include <cxxabi.h>
30 #include <sys/stat.h>
31 
32 #ifdef _GNU_SOURCE
33 # include <cstring>
34 # include <dlfcn.h>
35 #endif
36 
37 #ifdef USE_BOOST_FILESYSTEM
38 # include <boost/filesystem.hpp>
39 namespace fs = boost::filesystem;
40 #else
41 # include <filesystem>
42 namespace fs = std::filesystem;
43 #endif // USE_BOOST_FILESYSTEM
44 
45 #if __cplusplus >= 201703 || (__clang__ && __APPLE__)
46 # include <string_view>
47 #else
48 # include <experimental/string_view>
49 namespace std {
50  using experimental::string_view;
51 }
52 #endif
53 
54 #define REG_SCOPE_LOCK std::lock_guard<std::recursive_mutex> _guard( m_mutex );
55 
56 namespace {
57  std::mutex registrySingletonMutex;
58 }
59 #define SINGLETON_LOCK std::lock_guard<std::mutex> _guard( ::registrySingletonMutex );
60 
61 #include <algorithm>
62 
63 namespace {
64  struct OldStyleCnv {
65  std::string name;
66  void operator()( const char c ) {
67  switch ( c ) {
68  case '<':
69  case '>':
70  case ',':
71  case '(':
72  case ')':
73  case ':':
74  case '.':
75  name.push_back( '_' );
76  break;
77  case '&':
78  name.push_back( 'r' );
79  break;
80  case '*':
81  name.push_back( 'p' );
82  break;
83  case ' ':
84  break;
85  default:
86  name.push_back( c );
87  break;
88  }
89  }
90  };
92  std::string old_style_name( const std::string& name ) {
93  return std::for_each( name.begin(), name.end(), OldStyleCnv() ).name;
94  }
95 } // namespace
96 
97 namespace Gaudi {
98  namespace PluginService {
99  GAUDI_PLUGIN_SERVICE_V2_INLINE namespace v2 {
100  namespace Details {
101  std::string demangle( const std::string& id ) {
102  int status;
103  auto realname = std::unique_ptr<char, decltype( free )*>(
104  abi::__cxa_demangle( id.c_str(), nullptr, nullptr, &status ), free );
105  if ( !realname ) return id;
106 #if _GLIBCXX_USE_CXX11_ABI
107  return std::regex_replace(
108  realname.get(),
109  std::regex{"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >( (?=>))?"},
110  "std::string" );
111 #else
112  return std::string{realname.get()};
113 #endif
114  }
115  std::string demangle( const std::type_info& id ) { return demangle( id.name() ); }
116 
117  Registry& Registry::instance() {
119  static Registry r;
120  return r;
121  }
122 
123  void reportBadAnyCast( const std::type_info& factory_type, const std::string& id ) {
124  if ( logger().level() <= Logger::Debug ) {
125  std::stringstream msg;
126  const auto& info = Registry::instance().getInfo( id );
127  msg << "bad any_cast: requested factory " << id << " of type " << demangle( factory_type ) << ", got ";
128  if ( info.is_set() )
129  msg << demangle( info.factory.type() ) << " from " << info.library;
130  else
131  msg << "nothing";
132  logger().debug( msg.str() );
133  }
134  }
135 
136  Registry::Properties::mapped_type Registry::FactoryInfo::getprop( const Properties::key_type& name ) const {
137  auto p = properties.find( name );
138  return ( p != end( properties ) ) ? p->second : Properties::mapped_type{};
139  }
140 
141  Registry::Registry() {}
142 
143  void Registry::initialize() {
145 #if defined( _WIN32 )
146  const std::string envVar = "PATH";
147  const std::string sep = ";";
148 #elif defined( __APPLE__ )
149  const std::string envVar = "DYLD_LIBRARY_PATH";
150  const std::string sep = ":";
151 #else
152  const std::string envVar = "LD_LIBRARY_PATH";
153  const std::string sep = ":";
154 #endif
155 
156  std::regex line_format{"^(?:[[:space:]]*(?:(v[0-9]+)::)?([^:]+):(.*[^[:space:]]))?[[:space:]]*(?:#.*)?$"};
157  std::smatch matches;
158 
159  std::string search_path;
160  const char* envPtr = std::getenv( envVar.c_str() );
161  if ( envPtr ) search_path = envPtr;
162  if ( search_path.empty() ) {
163  return;
164  }
165 
166  logger().debug("searching factories in " + envVar);
167  logger().debug("searching factories in " + search_path);
168 
169  std::vector<std::string> directories;
170  boost::split(directories, search_path, boost::is_any_of(sep));
171 
172  for(fs::path dirName: directories) {
173  if ( not is_directory( dirName ) ) {
174  continue;
175  }
176  logger().debug( " looking into " + dirName.string() );
177  for ( auto& p : fs::directory_iterator( dirName ) ) {
178  // look for files called "*.components" in the directory
179  if ( p.path().extension() == ".components" && is_regular_file( p.path() ) ) {
180  // read the file
181  const auto& fullPath = p.path().string();
182  logger().debug( " reading " + p.path().filename().string() );
183  std::ifstream factories{fullPath};
184  std::string line;
185  int factoriesCount = 0;
186  int lineCount = 0;
187  while ( !factories.eof() ) {
188  ++lineCount;
189  std::getline( factories, line );
190  if ( regex_match( line, matches, line_format ) ) {
191  if ( matches[1] == "v2" ) { // ignore non "v2" and "empty" lines
192  const std::string lib{matches[2]};
193  const std::string fact{matches[3]};
194  m_factories.emplace( fact, FactoryInfo{lib, {}, {{"ClassName", fact}}} );
195 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
196  // add an alias for the factory using the Reflex convention
197  std::string old_name = old_style_name( fact );
198  if ( fact != old_name ) {
199  m_factories.emplace( old_name,
200  FactoryInfo{lib, {}, {{"ReflexName", "true"}, {"ClassName", fact}}} );
201  }
202 #endif
203  ++factoriesCount;
204  }
205  } else {
206  logger().debug( "failed to parse line " + fullPath + ':' + std::to_string( lineCount ) );
207  }
208  }
209  if ( logger().level() <= Logger::Debug ) {
210  logger().debug( " found " + std::to_string( factoriesCount ) + " factories" );
211  }
212  }
213  }
214  }
215  }
216 
217  const Registry::FactoryMap& Registry::factories() const {
218  std::call_once( m_initialized, &Registry::initialize, const_cast<Registry*>( this ) );
219  return m_factories;
220  }
221 
222  Registry::FactoryMap& Registry::factories() {
223  std::call_once( m_initialized, &Registry::initialize, this );
224  return m_factories;
225  }
226 
227  Registry::FactoryInfo& Registry::add( const KeyType& id, FactoryInfo info ) {
229  FactoryMap& facts = factories();
230 
231 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
232  // add an alias for the factory using the Reflex convention
233  const auto old_name = old_style_name( id );
234  if ( id != old_name ) {
235  auto new_info = info;
236 
237  new_info.properties["ReflexName"] = "true";
238 
239  add( old_name, new_info );
240  }
241 #endif
242 
243  auto entry = facts.find( id );
244  if ( entry == facts.end() ) {
245  // this factory was not known yet
246  entry = facts.emplace( id, std::move( info ) ).first;
247  } else {
248  // do not replace an existing factory with a new one
249  if ( !entry->second.is_set() ) entry->second = std::move( info );
250  }
251  return entry->second;
252  }
253 
254  const Registry::FactoryInfo& Registry::getInfo( const KeyType& id, const bool load ) const {
256  static const FactoryInfo unknown = {"unknown"};
257  const FactoryMap& facts = factories();
258  auto f = facts.find( id );
259 
260  if ( f != facts.end() ) {
261  if ( load && !f->second.is_set() ) {
262  const std::string library = f->second.library;
263  if ( !dlopen( library.c_str(), RTLD_LAZY | RTLD_GLOBAL ) ) {
264  logger().warning( "cannot load " + library + " for factory " + id );
265  char* dlmsg = dlerror();
266  if ( dlmsg ) logger().warning( dlmsg );
267  return unknown;
268  }
269  f = facts.find( id ); // ensure that the iterator is valid
270  }
271  return f->second;
272  } else {
273  return unknown;
274  }
275  }
276 
277  Registry& Registry::addProperty( const KeyType& id, const KeyType& k, const std::string& v ) {
279  FactoryMap& facts = factories();
280  auto f = facts.find( id );
281 
282  if ( f != facts.end() ) f->second.properties[k] = v;
283  return *this;
284  }
285 
286  std::set<Registry::KeyType> Registry::loadedFactoryNames() const {
288  std::set<KeyType> l;
289  for ( const auto& f : factories() ) {
290  if ( f.second.is_set() ) l.insert( f.first );
291  }
292  return l;
293  }
294 
295  void Logger::report( Level lvl, const std::string& msg ) {
296  static const char* levels[] = {"DEBUG : ", "INFO : ", "WARNING: ", "ERROR : "};
297  if ( lvl >= level() ) { std::cerr << levels[lvl] << msg << std::endl; }
298  }
299 
300  static auto s_logger = std::make_unique<Logger>();
301  Logger& logger() { return *s_logger; }
302  void setLogger( Logger* logger ) { s_logger.reset( logger ); }
303 
304  // This chunk of code was taken from GaudiKernel (genconf) DsoUtils.h
305  std::string getDSONameFor( void* fptr ) {
306 #ifdef _GNU_SOURCE
307  Dl_info info;
308  if ( dladdr( fptr, &info ) == 0 ) return "";
309 
310  auto pos = std::strrchr( info.dli_fname, '/' );
311  if ( pos )
312  ++pos;
313  else
314  return info.dli_fname;
315  return pos;
316 #else
317  return "";
318 #endif
319  }
320  } // namespace Details
321 
322  void SetDebug( int debugLevel ) {
323  using namespace Details;
324  Logger& l = logger();
325  if ( debugLevel > 1 )
326  l.setLevel( Logger::Debug );
327  else if ( debugLevel > 0 )
328  l.setLevel( Logger::Info );
329  else
330  l.setLevel( Logger::Warning );
331  }
332 
333  int Debug() {
334  using namespace Details;
335  switch ( logger().level() ) {
336  case Logger::Debug:
337  return 2;
338  case Logger::Info:
339  return 1;
340  default:
341  return 0;
342  }
343  }
344  }
345  } // namespace PluginService
346 } // namespace Gaudi
Gaudi::PluginService::v2::Details::logger
Logger & logger()
Definition: PluginServiceV2.cpp:301
GAUDI_PLUGIN_SERVICE_V2_INLINE
#define GAUDI_PLUGIN_SERVICE_V2_INLINE
Definition: PluginServiceCommon.h:20
v
View * v
Definition: MultiView.cpp:28
dd4hep::info
std::size_t info(const std::string &src, const std::string &msg)
Definition: RootDictionary.h:65
Gaudi::PluginService::v2::Details::demangle
std::string demangle(const std::string &id)
Definition: PluginServiceV2.cpp:101
Gaudi::PluginService::v2::Details::setLogger
void setLogger(Logger *logger)
Definition: PluginServiceV2.cpp:302
PluginService.h
Gaudi::PluginService::v2::Details::reportBadAnyCast
void reportBadAnyCast(const std::type_info &factory_type, const std::string &id)
Definition: PluginServiceV2.cpp:123
Gaudi::PluginService::v2::Debug
int Debug()
Definition: PluginServiceV2.cpp:333
SINGLETON_LOCK
#define SINGLETON_LOCK
Definition: PluginServiceV2.cpp:59
dd4hep::dd::split
std::vector< std::string_view > split(std::string_view, const char *)
Definition: Filter.cpp:94
std
Definition: Plugins.h:30
Gaudi
Definition: PluginServiceDetailsV1.h:27
Gaudi::PluginService::v2::Details::getDSONameFor
std::string getDSONameFor(void *fptr)
Definition: PluginServiceV2.cpp:305
Gaudi::PluginService::v2::SetDebug
void SetDebug(int debugLevel)
Definition: PluginServiceV2.cpp:322
REG_SCOPE_LOCK
#define REG_SCOPE_LOCK
Definition: PluginServiceV2.cpp:54