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