deployer-funcs.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  Copyright (c) 2008 S Roderick <xxxkiwi DOT xxxnet AT macxxx DOT comxxx>
3  (remove the x's above)
4 
5  ***************************************************************************
6  * This library is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU Lesser General Public *
8  * License as published by the Free Software Foundation; either *
9  * version 2.1 of the License, or (at your option) any later version. *
10  * *
11  * This library is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14  * Lesser General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU Lesser General Public *
17  * License along with this library; if not, write to the Free Software *
18  * Foundation, Inc., 59 Temple Place, *
19  * Suite 330, Boston, MA 02111-1307 USA *
20  ***************************************************************************/
21 
22 #include "deployer-funcs.hpp"
23 #include <rtt/Logger.hpp>
24 #include <cstdio>
25 #include <iostream>
26 #include <sstream>
27 #include <string>
28 #include <functional>
29 #include <boost/program_options.hpp>
30 #include <boost/program_options/positional_options.hpp>
31 #include <boost/assign/list_of.hpp>
32 #include <boost/algorithm/string/case_conv.hpp>
33 
34 #ifdef ORO_BUILD_RTALLOC
35 // need access to all TLSF functions embedded in RTT
36 #define ORO_MEMORY_POOL
37 //#define _DEBUG_TLSF_ 1
38 #include <rtt/os/tlsf/tlsf.h>
39 #ifdef _DEBUG_TLSF_
40  #include <boost/date_time/posix_time/posix_time.hpp> // with I/O
41  #include <fstream>
42 #endif
43 #endif
44 
45 #if defined(ORO_SUPPORT_CPU_AFFINITY)
46 #include <unistd.h>
47 #endif
48 
49 #if defined(ORO_BUILD_LOGGING) && defined(OROSEM_LOG4CPP_LOGGING)
50 // to configure RTT's use of log4cpp
51 #include <log4cpp/Category.hh>
52 #include <log4cpp/FileAppender.hh>
53 #include <log4cpp/PatternLayout.hh>
55 #endif
56 
57 namespace po = boost::program_options;
58 #ifdef _DEBUG_TLSF_
59 namespace bpt = boost::posix_time;
60 #endif
61 
62 using namespace RTT;
63 using namespace std;
64 
65 #define ORO_xstr(s) ORO_str(s)
66 #define ORO_str(s) #s
67 
68 namespace OCL
69 {
70 
71 // map lowercase strings to levels
72 std::map<std::string, RTT::Logger::LogLevel> logMap =
73  boost::assign::map_list_of
74  ("never", RTT::Logger::Debug)
75  ("fatal", RTT::Logger::Fatal)
76  ("critical", RTT::Logger::Critical)
77  ("error", RTT::Logger::Error)
78  ("warning", RTT::Logger::Warning)
79  ("info", RTT::Logger::Info)
80  ("debug", RTT::Logger::Debug)
81  ("realtime", RTT::Logger::RealTime);
82 
83 int deployerParseCmdLine(int argc,
84  char** argv,
85  std::string& siteFile,
86  std::vector<std::string>& scriptFiles,
87  std::string& name,
88  bool& requireNameService,
89  bool& deploymentOnlyChecked,
90  int& minNumberCPU,
91  po::variables_map& vm,
92  po::options_description* otherOptions)
93 {
94  std::string logLevel("info"); // set to valid default
95  po::options_description options;
96  po::options_description allowed("Allowed options");
97  po::positional_options_description pos;
98  allowed.add_options()
99  ("help,h",
100  "Show program usage")
101  ("version",
102  "Show program version")
103  ("daemon,d",
104  "Makes this program a daemon such that it runs in the background. Returns 1 in case of success.")
105  ("start,s",
106  po::value< std::vector<std::string> >(&scriptFiles),
107  "Deployment XML or script file (eg 'config-file.xml', 'script.ops' or 'script.lua')")
108  ("site-file",
109  po::value<std::string>(&siteFile),
110  "Site deployment XML file (eg 'Deployer-site.cpf' or 'Deployer-site.xml')")
111  ("log-level,l",
112  po::value<std::string>(&logLevel),
113  "Level at which to log from RTT (case-insensitive) Never,Fatal,Critical,Error,Warning,Info,Debug,Realtime")
114  ("no-consolelog",
115  "Turn off RTT logging to the console (will still log to 'orocos.log')")
116  ("check",
117  "Only check component loading, connecting peers and ports. Returns 255 in case of errors.")
118  ("require-name-service",
119  "Require CORBA name service")
120  ("minNumberCPU",
121  po::value<int>(&minNumberCPU),
122  "The minimum number of CPUs required for deployment (0 <= value) [0==no minimum (default)]")
123  ("DeployerName",
124  po::value< std::vector<std::string> >(),
125  "Name of deployer component (the --DeployerName flag is optional). If you provide a script or XML file name, that will be run instead.")
126  ;
127  pos.add("DeployerName", -1);
128 
129  // collate options
130  options.add(allowed);
131  if (NULL != otherOptions)
132  {
133  options.add(*otherOptions);
134  }
135 
136  try
137  {
138  po::store(po::command_line_parser(argc, argv).
139  options(options).positional(pos).run(),
140  vm);
141  po::notify(vm);
142 
143  // deal with options
144  if (vm.count("help"))
145  {
146  std::cout << options << std::endl;
147  return 1;
148  }
149 
150  // version info
151  if (vm.count("version"))
152  {
153  std::cout<< " OROCOS Toolchain version '" ORO_xstr(RTT_VERSION) "'";
154 #ifdef __GNUC__
155  std::cout << " ( GCC " ORO_xstr(__GNUC__) "." ORO_xstr(__GNUC_MINOR__) "." ORO_xstr(__GNUC_PATCHLEVEL__) " )";
156 #endif
157 #ifdef OROPKG_OS_LXRT
158  std::cout<<" -- LXRT/RTAI.";
159 #endif
160 #ifdef OROPKG_OS_GNULINUX
161  std::cout<<" -- GNU/Linux.";
162 #endif
163 #ifdef OROPKG_OS_XENOMAI
164  std::cout<<" -- Xenomai.";
165 #endif
166  std::cout << endl;
167  return 1;
168  }
169 
170  if (vm.count("daemon"))
171  {
172  if (vm.count("check"))
173  log(Warning) << "--check and --daemon are incompatible. Skipping the --daemon flag." <<endlog();
174  else
175  if (fork() != 0 )
176  return 1;
177  }
178 
179  if ( !(0 <= minNumberCPU) )
180  {
181  std::cout << std::endl
182  << "ERROR: Invalid minimum number CPU. Require 0 <= value"
183  << std::endl << options << std::endl;
184  return -2;
185  }
186 
187  // turn off all console logging
188  if (vm.count("no-consolelog"))
189  {
191  log(Info) << "Console logging disabled" << endlog();
192  }
193 
194  if (vm.count("check"))
195  {
196  deploymentOnlyChecked = true;
197  log(Info) << "Deployment check: Only check component loading, connecting peers and ports. Returns 255 in case of errors." << endlog();
198  }
199 
200  if (vm.count("require-name-service"))
201  {
202  requireNameService = true;
203  log(Info) << "CORBA name service required." << endlog();
204  }
205 
206  // verify that is a valid logging level
207  logLevel = boost::algorithm::to_lower_copy(logLevel); // always lower case
208  if (vm.count("log-level"))
209  {
210  if (0 != logMap.count(logLevel))
211  {
212  RTT::Logger::Instance()->setLogLevel(logMap[logLevel]);
213  }
214  else
215  {
216  std::cout << "Did not understand log level: "
217  << logLevel << std::endl
218  << options << std::endl;
219  return -1;
220  }
221  }
222  if (vm.count("DeployerName")) {
223  const std::vector<std::string> &positional_arguments = vm["DeployerName"].as< std::vector<std::string> >();
224  for(std::vector<std::string>::const_iterator it = positional_arguments.begin(); it != positional_arguments.end(); ++it) {
225  if (it->size() >= 1 && it->at(0) == '_') continue; // ignore arguments that start with a _
226  std::string arg = *it;
227  if (arg.rfind(".xml") != std::string::npos ||
228  arg.rfind(".cpf") != std::string::npos ||
229  arg.rfind(".osd") != std::string::npos ||
230  arg.rfind(".ops") != std::string::npos ||
231  arg.rfind(".lua") != std::string::npos ) {
232  scriptFiles.push_back(arg);
233  } else {
234  name = arg;
235  }
236  }
237  }
238  }
239  catch (std::logic_error e)
240  {
241  std::cerr << "Exception:" << e.what() << std::endl << options << std::endl;
242  return -1;
243  }
244 
245  return 0;
246 }
247 
248 int enforceMinNumberCPU(const int minNumberCPU)
249 {
250  assert(0 <= minNumberCPU);
251 
252  // enforce min CPU constraints
253  if (0 < minNumberCPU)
254  {
255 #if defined(ORO_SUPPORT_CPU_AFFINITY)
256 #ifdef __linux__
257  int numCPU = sysconf( _SC_NPROCESSORS_ONLN );
258 #else
259 #error Unsupported configuration!
260 #endif
261  if (-1 == numCPU)
262  {
263  std::cerr << "ERROR: Unable to determine the number of CPUs for minimum number CPU support."
264  << std::endl;
265  return -1;
266  }
267  else if (numCPU < minNumberCPU)
268  {
269  std::cerr << "ERROR: Number of CPUS (" << numCPU
270  << ") is less than minimum required (" << minNumberCPU
271  << ")" << std::endl;
272  return -2;
273  }
274  // else ok as numCPU <= minNumberCPU
275 
276 #else
277  std::cout << "WARNING: Ignoring minimum number of CPU requirement "
278  << "as RTT does not support CPU affinity on this platform."
279  << std::endl;
280 #endif
281  }
282 
283  return 0;
284 }
285 
286 #ifdef ORO_BUILD_RTALLOC
287 
288 void validate(boost::any& v,
289  const std::vector<std::string>& values,
290  memorySize* target_type, int)
291 {
292 // using namespace boost::program_options;
293 
294  // Make sure no previous assignment to 'a' was made.
295  po::validators::check_first_occurrence(v);
296  // Extract the first string from 'values'. If there is more than
297  // one string, it's an error, and exception will be thrown.
298  const std::string& memSize = po::validators::get_single_string(values);
299 
300  /* parse the string. Support "number" or "numberX" where
301  X is one of {k,K,m,M,g,G} with the expected meaning of X
302 
303  e.g.
304 
305  1024, 1k, 3.4M, 4.5G
306  */
307  float value=0;
308  char units='\0';
309  if (2 == sscanf(memSize.c_str(), "%f%c", &value, &units))
310  {
311  float multiplier=1;
312  if (islower(units))
313  {
314  units = toupper(units);
315  }
316  switch (units)
317  {
318  case 'G':
319  multiplier *= 1024;
320  // fall through
321  case 'M':
322  multiplier *= 1024;
323  // fall through
324  case 'K':
325  multiplier *= 1024;
326  break;
327  default:
328  std::stringstream e;
329  e << "Invalid units in rtalloc-mem-size option: " << memSize
330  << ". Valid units: 'k','m','g' (case-insensitive).";
331  throw po::invalid_option_value(e.str());
332  }
333  value *= multiplier;
334  }
335  else if (1 == sscanf(memSize.c_str(), "%f", &value))
336  {
337  // nothing to do
338  }
339  else
340  {
341  throw po::invalid_option_value("Could not parse rtalloc-mem-size option: " + memSize);
342  }
343 
344  // provide some basic sanity checking
345  // Note that TLSF has its own internal checks on the value.
346  // TLSF's minimum size varies with build options, but it is
347  // several kilobytes at least (6-8k on Mac OS X Snow Leopard
348  // with 64-bit build, ~3k on Ubuntu Jaunty with 32-bit build).
349  if (! (0 <= value) )
350  {
351  std::stringstream e;
352  e << "Invalid memory size of " << value << " given. Value must be >= 0.";
353  throw po::invalid_option_value(e.str());
354  }
355 
356  v = memorySize((size_t)value);
357 }
358 
359 
360 boost::program_options::options_description deployerRtallocOptions(memorySize& rtallocMemorySize)
361 {
362  boost::program_options::options_description rtallocOptions("Real-time memory allocation options");
363  rtallocOptions.add_options()
364  ("rtalloc-mem-size",
365  po::value<memorySize>(&rtallocMemorySize)->default_value(rtallocMemorySize),
366  "Amount of memory to provide for real-time memory allocations (e.g. 10000, 1K, 4.3m)\n"
367  "NB the minimum size depends on TLSF build options, but it is several kilobytes.")
368  ;
369  return rtallocOptions;
370 }
371 
372 TLSFMemoryPool::TLSFMemoryPool() :
373  rtMem(0)
374 {}
375 
376 TLSFMemoryPool::~TLSFMemoryPool()
377 {
378  shutdown();
379 }
380 
381 bool TLSFMemoryPool::initialize(const size_t memSize)
382 {
383  if (0 != rtMem) return false; // avoid double init
384  if (0 >= memSize) return false; // invalid size
385 
386  // don't calloc() as is first thing TLSF does.
387  rtMem = malloc(memSize);
388  assert(0 != rtMem);
389  const size_t freeMem = init_memory_pool(memSize, rtMem);
390  if ((size_t)-1 == freeMem)
391  {
392  std::cerr << std::dec
393  << "Invalid memory pool size of " << memSize
394  << " bytes (TLSF has a several kilobyte overhead)." << std::endl;
395  free(rtMem);
396  rtMem = 0;
397  return false;
398  }
399  std::cout << std::dec
400  << "Real-time memory: " << freeMem << " bytes free of "
401  << memSize << " allocated." << std::endl;
402  return true;
403 }
404 
405 void TLSFMemoryPool::shutdown()
406 {
407  if (0 != rtMem)
408  {
409  // only dump TLSF debug if env. var. is set
410  if (NULL != getenv("ORO_DEPLOYER_DUMP_TLSF_ON_SHUTDOWN"))
411  {
412  OCL::deployerDumpTLSF();
413  }
414 
415  const size_t overhead = get_overhead_size(rtMem);
416  std::cout << std::dec
417  << "TLSF bytes allocated=" << get_pool_size(rtMem)
418  << " overhead=" << overhead
419  << " max-used=" << (get_max_size(rtMem) - overhead)
420  << " still-allocated=" << (get_used_size(rtMem) - overhead)
421  << std::endl;
422 
423  destroy_memory_pool(rtMem);
424  free(rtMem);
425  rtMem = 0;
426  }
427 }
428 
429 void deployerDumpTLSF()
430 {
431 #ifdef _DEBUG_TLSF_
432  std::ofstream file;
433 
434  // format now as "YYYYMMDDTHHMMSS.ffffff"
435  const bpt::ptime now = bpt::microsec_clock::local_time();
436  static const size_t START = strlen("YYYYMMDDTHHMMSS,");
437  std::string now_s = bpt::to_iso_string(now); // YYYYMMDDTHHMMSS,fffffffff
438  now_s.replace(START-1, 1, "."); // replace "," with "."
439  now_s.erase(now_s.size()-3, 3); // from nanosec to microsec
440 
441  // TLSF debug
442  FILE* ff = fopen("deployer_tlsf_dump.txt", "a"); // write+append
443  if (0 != ff)
444  {
445  fprintf(ff, "# Log at %s\n", now_s.c_str());
446  print_all_blocks_mp(ff);
447  (void)fclose(ff);
448  ff=0;
449  }
450 #endif
451 }
452 
453 #endif // ORO_BUILD_RTALLOC
454 
455 
456 #if defined(ORO_BUILD_LOGGING) && defined(OROSEM_LOG4CPP_LOGGING)
457 
458 boost::program_options::options_description deployerRttLog4cppOptions(std::string& rttLog4cppConfigFile)
459 {
460  po::options_description rttLog4cppOptions("RTT/Log4cpp options");
461  rttLog4cppOptions.add_options()
462  ("rtt-log4cpp-config-file",
463  po::value<std::string>(&rttLog4cppConfigFile),
464  std::string("Log4cpp configuration file to support RTT category '" + RTT::Logger::log4cppCategoryName + "'\n"+
465  "WARNING Configure only this category. Use deployment files to configure realtime logging!").c_str())
466  ;
467  return rttLog4cppOptions;
468 }
469 
470 int deployerConfigureRttLog4cppCategory(const std::string& rttLog4cppConfigFile)
471 {
472  // configure where RTT::Logger's file logging goes to (which is through
473  // log4cpp, but not through OCL's log4cpp-derived real-time logging!)
474  if (!rttLog4cppConfigFile.empty())
475  {
476  try
477  {
478  log4cpp::PropertyConfigurator::configure(rttLog4cppConfigFile);
479  }
480  catch (log4cpp::ConfigureFailure& e)
481  {
482  std::cerr << "ERROR: Unable to read/parse log4cpp configuration file\n"
483  << e.what() << std::endl;
484  return false;
485  }
486  }
487  else
488  {
489  // setup default of logging to 'orocos.log' file
490  log4cpp::PatternLayout* layout=0;
491  log4cpp::Appender* appender=0;
492  appender = new log4cpp::FileAppender(RTT::Logger::log4cppCategoryName,
493  "orocos.log");
494 
495  layout = new log4cpp::PatternLayout();
496  // encode as (ISO date) "yyyymmddTHH:MM:SS.ms category message"
497  layout->setConversionPattern("%d{%Y%m%dT%T.%l} [%p] %m%n");
498  appender->setLayout(layout);
499 
500  log4cpp::Category& category =
501  log4cpp::Category::getInstance(RTT::Logger::log4cppCategoryName);
502  category.setAppender(appender);
503  // appender and layout are now owned by category - do not delete!
504  }
505  return true;
506 }
507 
508 #endif // OROSEM_LOG4CPP_LOGGING
509 
510 // namespace
511 }
size_t get_max_size(void *mem_pool)
static Logger * Instance(std::ostream &str=std::cerr)
static Category & getInstance(const std::string &name)
size_t init_memory_pool(size_t mem_pool_size, void *mem_pool)
virtual void setConversionPattern(const std::string &conversionPattern)
void setAppender(Appender *appender)
int deployerParseCmdLine(int argc, char **argv, std::string &siteFile, std::vector< std::string > &scriptFiles, std::string &name, bool &requireNameService, bool &deploymentOnlyChecked, int &minNumberCPU, po::variables_map &vm, po::options_description *otherOptions)
void destroy_memory_pool(void *mem_pool)
void mayLogStdOut(bool tf)
std::map< std::string, RTT::Logger::LogLevel > logMap
basic_ostreams & endl(basic_ostreams &s)
size_t get_pool_size(void *mem_pool)
size_t get_overhead_size(void *mem_pool)
std::vector< typename MapT::mapped_type > values(const MapT &map)
#define ORO_xstr(s)
size_t get_used_size(void *mem_pool)
static Logger & log()
int enforceMinNumberCPU(const int minNumberCPU)
virtual void setLayout(Layout *layout)=0
static Logger::LogFunction endlog()
static void configure(const std::string &initFileName)
void setLogLevel(LogLevel ll)


ocl
Author(s): OCL Development Team
autogenerated on Wed Jun 26 2019 19:26:27