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