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