Salome HOME
[EDF27816] Activate squeeze memory by default
[modules/yacs.git] / src / yacsloader / driver.cxx
1 // Copyright (C) 2006-2023  CEA, EDF
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "yacsconfig.h"
21 #include "RuntimeSALOME.hxx"
22 #include "Proc.hxx"
23 #include "Logger.hxx"
24 #include "Exception.hxx"
25 #include "Executor.hxx"
26 #include "parsers.hxx"
27 #include "VisitorSalomeSaveState.hxx"
28 #include "VisitorSaveSalomeSchema.hxx"
29 #include "LoadState.hxx"
30 #include "Dispatcher.hxx"
31 #include "LinkInfo.hxx"
32 #include "ObserverAsPlugin.hxx"
33 #include "PythonNode.hxx"
34
35 #include "KernelBasis.hxx"
36 #include "SALOME_Launcher.hxx"
37 #include "ServiceUnreachable.hxx"
38 #include "SALOME_NamingService_Wrapper.hxx"
39 #include "SALOME_NamingService.hxx"
40 #include "SALOME_ModuleCatalog.hh"
41 #include "SALOMESDS_DataServerManager.hxx"
42 #include "Basics_Utils.hxx"
43
44 #include <iostream>
45 #include <fstream>
46 #include <signal.h>
47 #include <list>
48
49 #if defined WIN32 || defined __APPLE__
50 #else
51 #include <argp.h>
52 #endif
53
54 using YACS::YACSLoader;
55 using namespace YACS::ENGINE;
56 using namespace std;
57
58
59 // --- use of glibc argp interface for parsing unix-style arguments
60
61 const char *argp_program_version ="driver V0.1";
62 const char *argp_program_bug_address ="<nepal@nepal.edf.fr>";
63 static char doc[] ="driver -- a SALOME YACS graph executor";
64 static char args_doc[] = "graph.xml";
65
66 #if defined WIN32 || defined __APPLE__
67 #else
68 static struct argp_option options[] =
69   {
70     {"display",         'd', "level", 0,                   "Display dot files: 0=never to 3=very often (default 0)"},
71     {"verbose",         'v', 0,       0,                   "Produce verbose output" },
72     {"stop-on-error",   's', 0,       0,                   "Stop on first error" },
73     {"dump-on-error",   'e', "file",  OPTION_ARG_OPTIONAL, "Stop on first error and dump state"},
74     {"dump-final",      'f', "file",  OPTION_ARG_OPTIONAL, "dump final state"},
75     {"dump",            'g', "nbsec", OPTION_ARG_OPTIONAL, "dump state"},
76     {"load-state",      'l', "file",  0,                   "Load State from a previous partial execution"},
77     {"save-xml-schema", 'x', "file",  OPTION_ARG_OPTIONAL, "dump xml schema"},
78     {"shutdown",        't', "level", 0,                   "Shutdown the schema: 0=no shutdown to 3=full shutdown (default 1)"},
79     {"reset",           'r', "level", 0,                   "Reset the schema before execution: 0=nothing, 1=reset error nodes to ready state (default 0)"},
80     {"kill-port",       'k', "port",  0,                   "Kill Salome application running on the specified port if the driver process is killed (with SIGINT or SIGTERM)"},
81     {"init_port",       'i', "value", OPTION_ARG_OPTIONAL, "Initialisation value of a port, specified as bloc.node.port=value."},
82     {"donotsqueeze",    'z', "value", 0,                   "Desactivate squeeze memory optimization."},
83     { nullptr }
84   };
85 #endif
86
87 struct arguments
88 {
89   char *args[1];
90   int display = 0;
91   int verbose = 0;
92   int stop = 0;
93   std::string dumpErrorFile;
94   std::string finalDump;
95   int dump = 0;
96   std::string xmlSchema;
97   std::string loadState;
98   int shutdown = 10;
99   int reset = 0;
100   int killPort = 0;
101   bool squeezeMemory = true;
102   std::list<std::string> init_ports;
103 };
104
105 typedef struct {
106   int nbsec;
107   string dumpFile;
108   string lockFile;
109 } thread_st;
110
111 #if defined WIN32 || defined __APPLE__
112 static int
113 #else
114 static error_t
115 #endif
116 parse_opt (int key, char *arg, struct argp_state *state)
117 {
118 #if defined WIN32 || defined __APPLE__
119 #else
120   // Get the input argument from argp_parse, which we
121   // know is a pointer to our arguments structure. 
122   struct arguments *myArgs = (arguments*)state->input;
123   
124   switch (key)
125     {
126     case 'd':
127       myArgs->display = atoi(arg);
128       break;
129     case 't':
130       myArgs->shutdown = atoi(arg);
131       break;
132     case 'r':
133       myArgs->reset = atoi(arg);
134       break;
135     case 'v':
136       myArgs->verbose = 1;
137       break;
138     case 's':
139       myArgs->stop = 1;
140       break;
141     case 'e':
142       myArgs->stop = 1;
143       if (arg)
144         myArgs->dumpErrorFile = arg;
145       else
146         myArgs->dumpErrorFile = "dumpErrorState.xml";
147       break;
148     case 'f':
149       if (arg)
150         myArgs->finalDump = arg;
151       else
152         myArgs->finalDump = "finalDumpState.xml";
153       break;      
154     case 'g':
155       if (arg)
156         myArgs->dump = atoi(arg);
157       else
158         myArgs->dump = 60;
159       break;      
160     case 'l':
161       myArgs->loadState = arg;
162       break;
163     case 'x':
164       if (arg)
165         myArgs->xmlSchema = arg;
166       else
167         myArgs->xmlSchema = "saveSchema.xml";
168       break;      
169     case 'k':
170       myArgs->killPort = atoi(arg);
171       break;
172     case 'i':
173       if (arg)
174         myArgs->init_ports.push_back(std::string(arg));
175       break;
176     case 'z':
177       myArgs->squeezeMemory = ! ( bool( atoi(arg) ) );
178       break;
179     case ARGP_KEY_ARG:
180       if (state->arg_num >=1) // Too many arguments.
181         argp_usage (state);
182       myArgs->args[state->arg_num] = arg;
183       break;
184       
185     case ARGP_KEY_END:
186       if (state->arg_num < 1) // Not enough arguments.
187         argp_usage (state);
188       break;
189      
190     default:
191       return ARGP_ERR_UNKNOWN;
192     }
193 #endif
194   return 0;
195 }
196
197 // Our argp parser.
198 #if defined WIN32 || defined __APPLE__
199 #else
200 static struct argp argp = { options, parse_opt, args_doc, doc };
201 #endif
202
203 void timer(std::string msg)
204 {
205 #if defined WIN32 || defined __APPLE__
206 #else
207   struct timeval tv;
208   gettimeofday(&tv,NULL);
209   long t=tv.tv_sec*1000+tv.tv_usec/1000;
210   static long t0=t;
211   gettimeofday(&tv,NULL);
212   std::cerr << msg << tv.tv_sec*1000+tv.tv_usec/1000-t0 << " ms" << std::endl;
213 #endif
214 }
215
216 Proc* p=0;
217 static struct arguments myArgs;
218
219 void Handler(int theSigId)
220 {
221   if(p)
222     {
223       p->cleanNodes();
224       //if requested save state
225       bool isFinalDump = !myArgs.finalDump.empty();
226       if (isFinalDump)
227         {
228           YACS::ENGINE::VisitorSalomeSaveState vst(p);
229           vst.openFileDump(myArgs.finalDump);
230           p->accept(&vst);
231           vst.closeFileDump();
232         }
233       //if requested shutdown schema
234       if(myArgs.shutdown < 999)
235         {
236           p->shutdown(myArgs.shutdown);
237         }
238     }
239   if (myArgs.killPort)
240     {
241       ostringstream command;
242       command << "killSalomeWithPort.py " << myArgs.killPort;
243       int status = system(command.str().c_str());
244       if (status == 0)
245         cerr << "Salome application on port " << myArgs.killPort << " is killed" << endl;
246       else
247         cerr << "Error: Can't kill Salome application on port " << myArgs.killPort << endl;
248     }
249   _exit(1);
250 }
251
252 void * dumpState(void *arg)
253 {
254   thread_st *st = (thread_st*)arg;
255   YACS::StatesForNode state = p->getEffectiveState();
256   while((state != YACS::DONE) && (state != YACS::LOADFAILED) && (state != YACS::EXECFAILED) && (state != YACS::INTERNALERR) && (state != YACS::DISABLED) && (state != YACS::FAILED) && (state != YACS::ERROR)){
257 #ifdef WIN32
258     Sleep(st->nbsec);
259 #else 
260     sleep(st->nbsec);
261 #endif
262     string cmd = "touch " + st->lockFile;
263     system(cmd.c_str());
264     YACS::ENGINE::VisitorSalomeSaveState vst(p);
265     vst.openFileDump(st->dumpFile);
266     p->accept(&vst);
267     vst.closeFileDump();
268     cmd = "rm -f " + st->lockFile;
269     system(cmd.c_str());
270     state = p->getEffectiveState();
271   }
272   delete st;
273   return NULL;
274 }
275
276 #ifndef WIN32
277 typedef void (*sighandler_t)(int);
278 sighandler_t setsig(int sig, sighandler_t handler)
279 {
280   struct sigaction context, ocontext;
281   context.sa_handler = handler;
282   sigemptyset(&context.sa_mask);
283   context.sa_flags = 0;
284   if (sigaction(sig, &context, &ocontext) == -1)
285     return SIG_ERR;
286   return ocontext.sa_handler;
287 }
288 #endif
289
290 bool parse_init_port(const std::string& input, std::string& node, std::string& port, std::string& value)
291 {
292   bool ok = true;
293   size_t pos_eq = input.find('=');
294   if(pos_eq == std::string::npos || pos_eq == input.size())
295     return false;
296   value = input.substr(pos_eq+1);
297   size_t pos_dot = input.rfind('.', pos_eq);
298   if(!pos_dot || pos_dot == std::string::npos || pos_dot >= pos_eq-1)
299     return false;
300   port = input.substr(pos_dot+1, pos_eq-pos_dot-1);
301   node = input.substr(0, pos_dot);
302   return true;
303 }
304
305 void InitializeSSL()
306 {
307   setSSLMode(true);
308   KERNEL::getLauncherSA();
309 }
310
311 void shutdownServers()
312 {
313   // shutdown data server scopes
314   try
315     {
316       YACS::ENGINE::RuntimeSALOME* runTime = YACS::ENGINE::getSALOMERuntime();
317       runTime->loadModulCatalog();
318       CORBA::ORB_ptr orb = runTime->getOrb();
319       if (orb)
320       {
321         SALOME_NamingService_Wrapper namingService(orb);
322         CORBA::Object_var objDSM(namingService.Resolve(SALOMESDS::DataServerManager::NAME_IN_NS));
323         SALOME::DataServerManager_var dsm(SALOME::DataServerManager::_narrow(objDSM));
324         if ( !CORBA::is_nil(dsm) )
325           dsm->shutdownScopes();
326       }
327     }
328   catch(const CORBA::Exception& )
329     {
330        // ignore and continue
331     }
332   catch(ServiceUnreachable& e)
333     {
334        // ignore and continue
335     }
336 }
337
338 int main (int argc, char* argv[])
339 {
340   // Parse our arguments; every option seen by parse_opt will be reflected in arguments.
341 #if defined WIN32 || defined __APPLE__
342 #else
343   argp_parse (&argp, argc, argv, 0, 0, &myArgs);
344   std::cerr << "graph = " << myArgs.args[0];
345   std::cerr << " options: display=" << myArgs.display;
346   std::cerr << " verbose="<<myArgs.verbose;
347   std::cerr << " stop-on-error=" << myArgs.stop;
348   std::cerr << " shutdown=" << myArgs.shutdown;
349   std::cerr << " reset=" << myArgs.reset;
350   if (myArgs.killPort)
351     std::cerr << " kill-port=" << myArgs.killPort;
352   if (myArgs.stop)
353     std::cerr << " dumpErrorFile=" << myArgs.dumpErrorFile << std::endl;
354   else
355     std::cerr << std::endl;
356   std::list<std::string>::const_iterator it;
357   for(it=myArgs.init_ports.begin(); it != myArgs.init_ports.end(); it++)
358   {
359     std::cerr << (*it) << std::endl;
360   }
361 #endif
362
363 #ifndef WIN32
364   setsig(SIGINT,&Handler);
365   setsig(SIGTERM,&Handler);
366 #endif
367
368   InitializeSSL();
369
370   timer("Starting ");
371   long flags = RuntimeSALOME::UsePython + RuntimeSALOME::UseCorba + RuntimeSALOME::UseXml + \
372                RuntimeSALOME::UseCpp + RuntimeSALOME::UseSalome;
373   RuntimeSALOME::setRuntime(flags, argc, argv);
374
375   // Try to load the session catalog if it exists
376   try
377     {
378       YACS::ENGINE::RuntimeSALOME* runTime = YACS::ENGINE::getSALOMERuntime();
379       runTime->loadModulCatalog();
380       CORBA::ORB_ptr orb = runTime->getOrb();
381       if (orb)
382         {
383           SALOME_NamingService_Wrapper namingService(orb);
384           CORBA::Object_var obj = namingService.Resolve("/Kernel/ModulCatalog");
385           SALOME_ModuleCatalog::ModuleCatalog_var aModuleCatalog = SALOME_ModuleCatalog::ModuleCatalog::_narrow(obj);
386           if (! CORBA::is_nil(aModuleCatalog))
387             {
388               CORBA::String_var anIOR = orb->object_to_string( aModuleCatalog );
389               YACS::ENGINE::Catalog* aCatalog = runTime->loadCatalog( "session", anIOR.in() );
390               runTime->addCatalog(aCatalog);
391             }
392         }
393     }
394   catch(ServiceUnreachable& e)
395     {
396       //Naming service unreachable don't add catalog
397     }
398
399   YACSLoader loader;
400   Executor executor;
401
402   try
403     {
404       timer("Elapsed time before load: ");
405       p=loader.load(myArgs.args[0]);
406       if(p==0)
407         {
408           std::cerr << "The imported file is probably not a YACS schema file" << std::endl;
409           return 1;
410         }
411       if(myArgs.squeezeMemory)
412       {
413         std::list<Node *> allNodes = p->getAllRecursiveNodes();
414         for(auto node : allNodes)
415         {
416           PythonNode *nodePy = dynamic_cast<PythonNode *>(node);
417           if( nodePy )
418           {
419             nodePy->setSqueezeStatus( true );
420           }
421         }
422       }
423       // Initialize the ports
424       for(std::list<std::string>::iterator it=myArgs.init_ports.begin(); it != myArgs.init_ports.end(); it++)
425       {
426         std::string node, port, value;
427         if(parse_init_port((*it), node, port, value))
428         {
429           std::cerr << "Initialization node=" << node
430                     << " port=" << port
431                     << " value=" << value << std::endl;
432
433           std::string init_state;
434           init_state = p->setInPortValue(node, port, value);
435           if(value.compare(init_state))
436           {
437             std::cerr << "Error on initialization:" << init_state << std::endl;
438             return 1;
439           }
440         }
441         else
442         {
443           std::cerr << "Error on parsing initialization string:" << (*it) << std::endl;
444           return 1;
445         }
446       }
447       
448       //Get the parser logger
449       Logger* logger=p->getLogger("parser");
450       //Print errors logged if any
451       if(!logger->isEmpty())
452         {
453           std::cerr << "The imported file has errors" << std::endl;
454           std::cerr << logger->getStr() << std::endl;
455         }
456       //Don't execute if there are errors
457       if(logger->hasErrors())
458         {
459           if(!p->isValid())
460             {
461               std::string report=p->getErrorReport();
462               std::cerr << "The schema is not valid and can not be executed" << std::endl;
463               std::cerr << report << std::endl;
464             }
465           delete p;
466           Runtime* r=YACS::ENGINE::getRuntime();
467           Dispatcher* disp=Dispatcher::getDispatcher();
468           r->fini();
469           delete r;
470           delete disp;
471           return 1;
472         }
473       timer("Elapsed time after load: ");
474
475       if(!p->isValid())
476         {
477           std::string report=p->getErrorReport();
478           std::cerr << "The schema is not valid and can not be executed" << std::endl;
479           std::cerr << report << std::endl;
480           Runtime* r=YACS::ENGINE::getRuntime();
481           Dispatcher* disp=Dispatcher::getDispatcher();
482           r->fini();
483           delete r;
484           delete disp;
485           return 1;
486         }
487       timer("Elapsed time after validation: ");
488
489       // Check consistency
490       LinkInfo info(LinkInfo::ALL_DONT_STOP);
491       p->checkConsistency(info);
492       if(info.areWarningsOrErrors())
493         {
494           std::cerr << "The schema is not consistent and can not be executed" << std::endl;
495           std::cerr << info.getGlobalRepr() << std::endl;
496           Runtime* r=YACS::ENGINE::getRuntime();
497           Dispatcher* disp=Dispatcher::getDispatcher();
498           r->fini();
499           delete r;
500           delete disp;
501           return 1;
502         }
503       timer("Elapsed time after check consistency: ");
504
505       //execution
506       bool isXmlSchema = !myArgs.xmlSchema.empty() ;
507       if (isXmlSchema)
508       {
509         YACS::ENGINE::VisitorSaveSalomeSchema vss(p);
510         vss.openFileSchema(myArgs.xmlSchema);
511         p->accept(&vss);
512         vss.closeFileSchema();
513       }
514
515       bool fromScratch = myArgs.loadState.empty() ;
516       if (!fromScratch)
517         {
518           p->init();
519           p->exUpdateState();
520           stateParser* rootParser = new stateParser();
521           stateLoader myStateLoader(rootParser, p);
522           myStateLoader.parse(myArgs.loadState);
523           if(myArgs.reset>0)
524             {
525               p->resetState(myArgs.reset);
526               p->exUpdateState();
527             }
528         }
529
530       if (myArgs.stop)
531       {
532         if ( !myArgs.dumpErrorFile.empty() )
533           executor.setStopOnError(true, myArgs.dumpErrorFile);
534         else
535           executor.setStopOnError(false, myArgs.dumpErrorFile);
536       }
537       if(myArgs.display>0)
538         {
539           std::ofstream f("toto");
540           p->writeDot(f);
541           f.close();
542         }
543
544       bool isDump = (myArgs.dump != 0);
545       pthread_t th;
546       if (isDump)
547         {
548           thread_st *st = new thread_st;
549           st->nbsec = myArgs.dump;
550           st->dumpFile = string("dumpState_") + myArgs.args[0];
551           string rootFile = st->dumpFile.substr(0,st->dumpFile.find("."));
552           st->lockFile = rootFile + ".lock";
553           pthread_create(&th,NULL,&dumpState,(void*)st);
554         }
555       YACS::ENGINE::LoadObserversPluginIfAny(p,&executor);
556       cerr << "+++++++++++++++++++ start calculation +++++++++++++++++++" << endl;
557       executor.RunW(p,myArgs.display, fromScratch);
558       cerr << "+++++++++++++++++++  end calculation  +++++++++++++++++++" << endl;
559       cerr << "Proc state : " << Node::getStateName(p->getEffectiveState()) << endl;
560       timer("Elapsed time after execution: ");
561
562       // Return 0 if SCHEMA OK
563       // Return 1 for YACS ERROR (EXCEPTION NOT CATCHED)
564       // Return 2 for YACS SCHEMA ERROR/FAILED
565       int return_value = 0;
566
567       if(p->getEffectiveState() != YACS::DONE)
568         {
569           std::string report=p->getErrorReport();
570           std::cerr << "Execution has ended in error" << std::endl;
571           std::cerr << report << std::endl;
572           return_value = 2;
573         }
574
575       if(myArgs.display>0)
576         {
577           std::ofstream g("titi");
578           p->writeDot(g);
579           g.close();
580         }
581
582       if (isDump)
583         {
584           pthread_join(th,NULL);
585         }
586
587       bool isFinalDump = !myArgs.finalDump.empty() ;
588       if (isFinalDump)
589         {
590           YACS::ENGINE::VisitorSalomeSaveState vst(p);
591           vst.openFileDump(myArgs.finalDump);
592           p->accept(&vst);
593           vst.closeFileDump();
594         }
595       if(myArgs.shutdown < 999)
596         {
597           p->shutdown(myArgs.shutdown);
598           shutdownServers();
599         }
600       delete p;
601       Runtime* r=YACS::ENGINE::getRuntime();
602       Dispatcher* disp=Dispatcher::getDispatcher();
603       r->fini();
604       delete r;
605       delete disp;
606       YACS::ENGINE::UnLoadObserversPluginIfAny();
607       return return_value;
608     }
609   catch (YACS::Exception& e)
610     {
611       cerr << "Caught a YACS exception" << endl;
612       cerr << e.what() << endl;
613       Runtime* r=YACS::ENGINE::getRuntime();
614       Dispatcher* disp=Dispatcher::getDispatcher();
615       r->fini();
616       delete r;
617       delete disp;
618       return 1;
619     }
620   catch (const std::ios_base::failure&)
621     {
622       cerr << "Caught an io failure exception" << endl;
623       return 1;
624     }
625   catch(CORBA::SystemException& ex) 
626     {
627       cerr << "Caught a CORBA::SystemException.:" << __FILE__ << ":" << __LINE__ << ":" ;
628       CORBA::Any tmp;
629       tmp <<= ex;
630       CORBA::TypeCode_var tc = tmp.type();
631       const char *p = tc->name();
632       if ( *p != '\0' ) 
633         cerr <<p;
634       else  
635         cerr  << tc->id();
636       cerr << endl;
637       return 1;
638     }
639   catch(omniORB::fatalException& fe) 
640     {
641       cerr << "Caught omniORB::fatalException:" << endl;
642       cerr << "  file: " << fe.file() << endl;
643       cerr << "  line: " << fe.line() << endl;
644       cerr << "  mesg: " << fe.errmsg() << endl;
645       return 1;
646     }
647   catch(...) 
648     {
649       cerr << "Caught unknown exception." << endl;
650       return 1;
651     }
652 }
653