0031687: Draw Harness, ViewerTest - extend command vrenderparams with option updating...
[occt.git] / src / Draw / Draw_BasicCommands.cxx
1 // Created on: 1995-02-23
2 // Created by: Remi LEQUETTE
3 // Copyright (c) 1995-1999 Matra Datavision
4 // Copyright (c) 1999-2014 OPEN CASCADE SAS
5 //
6 // This file is part of Open CASCADE Technology software library.
7 //
8 // This library is free software; you can redistribute it and/or modify it under
9 // the terms of the GNU Lesser General Public License version 2.1 as published
10 // by the Free Software Foundation, with special exception defined in the file
11 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
12 // distribution for complete text of the license and disclaimer of any warranty.
13 //
14 // Alternatively, this file may be used under the terms of Open CASCADE
15 // commercial license or contractual agreement.
16
17
18 #include <Draw.hxx>
19 #include <Draw_Appli.hxx>
20 #include <Draw_Chronometer.hxx>
21 #include <Draw_Drawable3D.hxx>
22 #include <Draw_Printer.hxx>
23 #include <Draw_ProgressIndicator.hxx>
24 #include <Message.hxx>
25 #include <Message_Messenger.hxx>
26 #include <OSD.hxx>
27 #include <OSD_Chronometer.hxx>
28 #include <OSD_Environment.hxx>
29 #include <OSD_Exception_CTRL_BREAK.hxx>
30 #include <OSD_MAllocHook.hxx>
31 #include <OSD_MemInfo.hxx>
32 #include <OSD_Parallel.hxx>
33 #include <OSD_ThreadPool.hxx>
34 #include <Standard_Macro.hxx>
35 #include <Standard_SStream.hxx>
36 #include <Standard_Stream.hxx>
37 #include <Standard_Version.hxx>
38 #include <TCollection_AsciiString.hxx>
39
40 #include <OSD_PerfMeter.h>
41 #ifdef _WIN32
42
43 #include <windows.h>
44 #include <winbase.h>
45 #include <process.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <time.h>
49 #include <limits>
50
51 #define RLIM_INFINITY   0x7fffffff
52
53 static clock_t CPU_CURRENT; // cpu time already used at last
54                             // cpulimit call. (sec.) 
55 #else /* _WIN32 */
56
57 #include <sys/resource.h>
58 #include <signal.h>
59 #include <unistd.h>
60
61 #if defined (__hpux) || defined ( HPUX )
62 #define RLIM_INFINITY   0x7fffffff
63 #define RLIMIT_CPU      0
64 #endif
65
66 #endif /* _WIN32 */
67
68 extern Standard_Boolean Draw_Batch;
69
70 static clock_t CPU_LIMIT;   // Cpu_limit in Sec.
71 static OSD_Timer aTimer;
72
73 //=======================================================================
74 // chronom
75 //=======================================================================
76
77 extern Standard_Boolean Draw_Chrono;
78
79 static Standard_Integer chronom(Draw_Interpretor& di,
80                                 Standard_Integer n,const char** a)
81 {
82   if ((n == 1) || (*a[1] == '0') || (*a[1] == '1')) {
83     if (n == 1)
84       Draw_Chrono = !Draw_Chrono;
85     else
86       Draw_Chrono = (*a[1] == '1');
87
88     if (Draw_Chrono) di << "Chronometers activated.\n";
89     else di << "Chronometers desactivated.\n";
90   }
91   else {
92     Handle(Draw_Drawable3D) D = Draw::Get(a[1]);
93     Handle(Draw_Chronometer) C;
94     if (!D.IsNull()) {
95       C = Handle(Draw_Chronometer)::DownCast(D);
96     }
97     if (C.IsNull()) {
98       C = new Draw_Chronometer();
99     Draw::Set(a[1],C,Standard_False);
100     }
101     if (n <= 2) {
102       C->Timer().Reset();
103     }
104     else {
105       for (Standard_Integer anIter = 2; anIter < n; ++anIter)
106       {
107         TCollection_AsciiString anArg (a[anIter]);
108         anArg.LowerCase();
109
110         if (anArg == "reset")
111         {
112           C->Timer().Reset();
113         }
114         else if (anArg == "restart")
115         {
116           C->Timer().Restart();
117         }
118         else if (anArg == "start")
119         {
120           C->Timer().Start();
121         }
122         else if (anArg == "stop")
123         {
124           C->Timer().Stop();
125         }
126         else if (anArg == "show")
127         {
128           C->Timer().Show();
129         }
130         else if (anArg == "counter")
131         {
132           Standard_Real aSeconds,aCPUtime;
133           Standard_Integer aMinutes, aHours;
134           C->Timer().Show(aSeconds,aMinutes,aHours,aCPUtime);
135           std::cout << "COUNTER " << a[++anIter] << ": " << aCPUtime << "\n";
136         }
137         else
138         {
139           std::cerr << "Unknown argument '" << a[anIter] << "'!\n";
140         }
141       }
142     }
143   }
144   return 0;
145 }
146
147 static Standard_Integer dchronom(Draw_Interpretor& theDI,
148                                  Standard_Integer n,const char** a)
149 {
150   if ((n == 1) || (*a[1] == '0') || (*a[1] == '1')) {
151     if (n == 1)
152       Draw_Chrono = !Draw_Chrono;
153     else
154       Draw_Chrono = (*a[1] == '1');
155
156     if (Draw_Chrono) theDI << "Chronometers activated.\n";
157     else theDI << "Chronometers desactivated.\n";
158   }
159   else {
160     Handle(Draw_Drawable3D) D = Draw::Get(a[1]);
161     Handle(Draw_Chronometer) C;
162     if (!D.IsNull()) {
163       C = Handle(Draw_Chronometer)::DownCast(D);
164     }
165     if (C.IsNull()) {
166       C = new Draw_Chronometer();
167       Draw::Set(a[1],C,Standard_False);
168     }
169     if (n <= 2) {
170       C->Timer().Reset();
171     }
172     else {
173       for (Standard_Integer anIter = 2; anIter < n; ++anIter)
174       {
175         TCollection_AsciiString anArg (a[anIter]);
176         anArg.LowerCase();
177
178         if (anArg == "reset")
179         {
180           C->Timer().Reset();
181         }
182         else if (anArg == "restart")
183         {
184           C->Timer().Restart();
185         }
186         else if (anArg == "start")
187         {
188           C->Timer().Start();
189         }
190         else if (anArg == "stop")
191         {
192           C->Timer().Stop();
193         }
194         else if (anArg == "show")
195         {
196           Standard_SStream ss;
197           C->Timer().Show(ss);
198           theDI << ss;
199         }
200         else if (anArg == "counter")
201         {
202           Standard_Real aSeconds,aCPUtime;
203           Standard_Integer aMinutes, aHours;
204           C->Timer().Show(aSeconds,aMinutes,aHours,aCPUtime);
205           theDI << "COUNTER " << a[++anIter] << ": " << aCPUtime << "\n";
206         }
207         else
208         {
209           theDI << "Unknown argument '" << a[anIter] << "'!\n";
210         }
211       }
212     }
213   }
214   return 0;
215 }
216
217
218
219 //=======================================================================
220 //function : ifbatch
221 //purpose  : 
222 //=======================================================================
223
224 static Standard_Integer ifbatch(Draw_Interpretor& DI, Standard_Integer , const char** )
225 {
226   if (Draw_Batch)
227     DI << "1";
228   else
229     DI << "0";
230   
231   return 0;
232 }
233
234 //=======================================================================
235 //function : spy
236 //purpose  : 
237 //=======================================================================
238
239 extern Standard_Boolean Draw_Spying;
240 extern std::filebuf Draw_Spyfile;
241
242 static Standard_Integer spy(Draw_Interpretor& di, Standard_Integer n, const char** a)
243 {
244   if (Draw_Spying) 
245     Draw_Spyfile.close();
246   Draw_Spying = Standard_False;
247   if (n > 1) {
248     if (!Draw_Spyfile.open(a[1],std::ios::out)) {
249       di << "Cannot open "<<a[1]<<" for writing\n";
250       return 1;
251     }
252     Draw_Spying = Standard_True;
253   }
254   return 0;
255 }
256
257 static Standard_Integer dlog(Draw_Interpretor& di, Standard_Integer n, const char** a)
258 {
259   if (n != 2 && n != 3)
260   {
261     std::cout << "Enable or disable logging: " << a[0] << " {on|off}" << std::endl;
262     std::cout << "Reset log: " << a[0] << " reset" << std::endl;
263     std::cout << "Get log content: " << a[0] << " get" << std::endl;
264     return 1;
265   }
266
267   if (! strcmp (a[1], "on") && n == 2)
268   {
269     di.SetDoLog (Standard_True);
270 //    di.Log() << "dlog on" << std::endl; // for symmetry
271   }
272   else if (! strcmp (a[1], "off") && n == 2)
273   {
274     di.SetDoLog (Standard_False);
275   }
276   else if (! strcmp (a[1], "reset") && n == 2)
277   {
278     di.ResetLog();
279   }
280   else if (! strcmp (a[1], "get") && n == 2)
281   {
282     di << di.GetLog();
283   }
284   else if (! strcmp (a[1], "add") && n == 3)
285   {
286     di.AddLog (a[2]);
287     di.AddLog ("\n");
288   }
289   else if (! strcmp (a[1], "status") && n == 2)
290   {
291     di << (di.GetDoLog() ? "on" : "off");
292   }
293   else {
294     std::cout << "Unrecognized option(s): " << a[1] << std::endl;
295     return 1;
296   }
297   return 0;
298 }
299
300 static Standard_Integer decho(Draw_Interpretor& di, Standard_Integer n, const char** a)
301 {
302   if (n != 2)
303   {
304     std::cout << "Enable or disable echoing: " << a[0] << " {on|off}" << std::endl;
305     return 1;
306   }
307
308   if (! strcmp (a[1], "on"))
309   {
310     di.SetDoEcho (Standard_True);
311   }
312   else if (! strcmp (a[1], "off"))
313   {
314     di.SetDoEcho (Standard_False);
315   }
316   else {
317     std::cout << "Unrecognized option: " << a[1] << std::endl;
318     return 1;
319   }
320   return 0;
321 }
322
323 static Standard_Integer dbreak(Draw_Interpretor& di, Standard_Integer, const char**)
324 {
325   try {
326     OSD::ControlBreak();
327   }
328   catch (OSD_Exception_CTRL_BREAK const&) {
329     di << "User pressed Control-Break";
330     return 1; // Tcl exception
331   }
332
333   return 0;
334 }
335
336 static Standard_Integer dversion(Draw_Interpretor& di, Standard_Integer, const char**)
337 {
338   // print OCCT version and OCCTY-specific macros used
339   di << "Open CASCADE Technology " << OCC_VERSION_STRING_EXT << "\n";
340 #ifdef OCCT_DEBUG
341   di << "Extended debug mode\n";
342 #elif defined(_DEBUG)
343   di << "Debug mode\n";
344 #endif
345 #ifdef HAVE_TBB
346   di << "TBB enabled (HAVE_TBB)\n";
347 #else 
348   di << "TBB disabled\n";
349 #endif
350 #ifdef HAVE_FREEIMAGE
351   di << "FreeImage enabled (HAVE_FREEIMAGE)\n";
352 #else
353   di << "FreeImage disabled\n";
354 #endif
355 #ifdef HAVE_FFMPEG
356   di << "FFmpeg enabled (HAVE_FFMPEG)\n";
357 #else
358   di << "FFmpeg disabled\n";
359 #endif
360 #ifdef HAVE_GLES2
361   di << "OpenGL: ES2\n";
362 #else
363   di << "OpenGL: desktop\n";
364 #endif
365 #ifdef HAVE_OPENVR
366   di << "OpenVR enabled (HAVE_OPENVR)\n";
367 #else
368   di << "OpenVR disabled\n";
369 #endif
370 #ifdef HAVE_RAPIDJSON
371   di << "RapidJSON enabled (HAVE_RAPIDJSON)\n";
372 #else
373   di << "RapidJSON disabled\n";
374 #endif
375 #ifdef HAVE_VTK
376   di << "VTK enabled (HAVE_VTK)\n";
377 #else
378   di << "VTK disabled\n";
379 #endif
380 #ifdef No_Exception
381   di << "Exceptions disabled (No_Exception)\n";
382 #else
383   di << "Exceptions enabled\n";
384 #endif
385
386   // check compiler, OS, etc. using pre-processor macros provided by compiler
387   // see "Pre-defined C/C++ Compiler Macros" http://sourceforge.net/p/predef/wiki/
388   // note that only modern compilers that are known to be used for OCCT are recognized
389
390   // compiler; note that GCC and MSVC are last as other compilers (e.g. Intel) can also define __GNUC__ and _MSC_VER
391 #if defined(__INTEL_COMPILER)
392   di << "Compiler: Intel " << __INTEL_COMPILER << "\n";
393 #elif defined(__BORLANDC__)
394   di << "Compiler: Borland C++ (__BORLANDC__ = " << __BORLANDC__ << ")\n";
395 #elif defined(__clang__)
396   di << "Compiler: Clang " << __clang_major__ << "." << __clang_minor__ << "." << __clang_patchlevel__ << "\n";
397 #elif defined(__SUNPRO_C)
398   di << "Compiler: Sun Studio (__SUNPRO_C = " << __SUNPROC_C << ")\n";
399 #elif defined(_MSC_VER)
400   #if _MSC_VER < 1900
401     di << "Compiler: MS Visual C++ " << (int)(_MSC_VER/100-6) << "." << (int)((_MSC_VER/10)-60-10*(int)(_MSC_VER/100-6)) << " (_MSC_FULL_VER = " << _MSC_FULL_VER << ")\n";
402   #else
403     di << "Compiler: MS Visual C++ " << (int)(_MSC_VER/100-5) << "." << (int)((_MSC_VER/10)-50-10*(int)(_MSC_VER/100-5)) << " (_MSC_FULL_VER = " << _MSC_FULL_VER << ")\n";
404   #endif
405 #elif defined(__GNUC__)
406   di << "Compiler: GCC " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__ << "\n";
407 #else
408   di << "Compiler: unrecognized\n";
409 #endif
410
411   // Cygwin and MinGW specifics
412 #if defined(__CYGWIN__)
413   di << "Cygwin\n";
414 #endif
415 #if defined(__MINGW64__)
416   di << "MinGW 64 " << __MINGW64_VERSION_MAJOR << "." << __MINGW64_VERSION_MINOR << "\n";
417 #elif defined(__MINGW32__)
418   di << "MinGW 32 " << __MINGW32_MAJOR_VERSION << "." << __MINGW32_MINOR_VERSION << "\n";
419 #endif 
420
421   // architecture
422 #if defined(__amd64) || defined(__x86_64) || defined(_M_AMD64)
423   di << "Architecture: AMD64\n";
424 #elif defined(__i386) || defined(_M_IX86) || defined(__X86__)|| defined(_X86_)
425   di << "Architecture: Intel x86\n";
426 #elif defined(_M_IA64) || defined(__ia64__)
427   di << "Architecture: Intel Itanium (IA 64)\n";
428 #elif defined(__sparc__) || defined(__sparc)
429   di << "Architecture: SPARC\n";
430 #else
431   di << "Architecture: unrecognized\n";
432 #endif
433
434   // OS
435 #if defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__)
436   di << "OS: Windows\n";
437 #elif defined(__APPLE__) || defined(__MACH__)
438   di << "OS: Mac OS X\n";
439 #elif defined(__sun) 
440   di << "OS: SUN Solaris\n";
441 #elif defined(__ANDROID__) /* must be before Linux */
442   #include <android/api-level.h>
443   di << "OS: Android (__ANDROID_API__ = " << __ANDROID_API__ << ")\n";
444 #elif defined(__QNXNTO__)
445   di << "OS: QNX Neutrino\n";
446 #elif defined(__QNX__)
447   di << "OS: QNX\n";
448 #elif defined(__linux__)
449   di << "OS: Linux\n";
450 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
451   #include <sys/param.h>
452   di << "OS: BSD (BSD = " << BSD << ")\n";
453 #else
454   di << "OS: unrecognized\n";
455 #endif
456
457   return 0;
458 }
459
460 //=======================================================================
461 //function : wait
462 //purpose  : 
463 //=======================================================================
464
465 static Standard_Integer Draw_wait(Draw_Interpretor& , Standard_Integer n, const char** a)
466 {
467   Standard_Integer w = 10;
468   if (n > 1)
469     w = Draw::Atoi(a[1]);
470   time_t ct = time(NULL) + w;
471   while (time(NULL) < ct) {};
472   return 0;
473 }
474
475 //=======================================================================
476 //function : cpulimit
477 //purpose  : 
478 //=======================================================================
479 #ifdef _WIN32
480 static unsigned int __stdcall CpuFunc (void * /*param*/)
481 {
482   clock_t anElapCurrent;
483   clock_t aCurrent;
484
485   for(;;)
486   {
487     Sleep (5);
488     Standard_Real anUserSeconds, aSystemSeconds;
489     OSD_Chronometer::GetProcessCPU (anUserSeconds, aSystemSeconds);
490     aCurrent = clock_t(anUserSeconds + aSystemSeconds);
491     anElapCurrent = clock_t(aTimer.ElapsedTime());
492     
493     if (CPU_LIMIT > 0 && (aCurrent - CPU_CURRENT) >= CPU_LIMIT)
494     {
495       aTimer.Stop();
496       if (IsDebuggerPresent())
497       {
498         std::cout << "Info: CPU limit (" << CPU_LIMIT << " sec) has been reached but ignored because of attached Debugger" << std::endl;
499         return 0;
500       }
501       else
502       {
503         std::cout << "ERROR: Process killed by CPU limit (" << CPU_LIMIT << " sec)" << std::endl;
504         ExitProcess (2);
505       }
506     }
507     if (CPU_LIMIT > 0 && anElapCurrent >= CPU_LIMIT)
508     {
509       aTimer.Stop();
510       if (IsDebuggerPresent())
511       {
512         std::cout << "Info: Elapsed limit (" << CPU_LIMIT << " sec) has been reached but ignored because of attached Debugger" << std::endl;
513         return 0;
514       }
515       else
516       {
517         std::cout << "ERROR: Process killed by elapsed limit (" << CPU_LIMIT << " sec)" << std::endl;
518         ExitProcess (2);
519       }
520     }
521   }
522 }
523 #else
524 static void cpulimitSignalHandler (int)
525 {
526   std::cout << "Process killed by CPU limit  (" << CPU_LIMIT << " sec)" << std::endl;
527   exit(2);
528 }
529 static void *CpuFunc(void* /*threadarg*/)
530 {
531   clock_t anElapCurrent;
532   for(;;)
533   {
534     sleep (5);
535     anElapCurrent = clock_t(aTimer.ElapsedTime());
536     if (CPU_LIMIT >0 && (anElapCurrent) >= CPU_LIMIT) {
537       std::cout << "Process killed by elapsed limit  (" << CPU_LIMIT << " sec)" << std::endl;
538       exit(2);
539     }
540   }
541   return NULL;
542 }
543 #endif
544
545 // Returns time in seconds defined by the argument string,
546 // multiplied by factor defined in environment variable
547 // CSF_CPULIMIT_FACTOR (if it exists, 1 otherwise)
548 static clock_t GetCpuLimit (const Standard_CString theParam)
549 {
550   clock_t aValue = Draw::Atoi (theParam);
551
552   OSD_Environment aEnv("CSF_CPULIMIT_FACTOR");
553   TCollection_AsciiString aEnvStr = aEnv.Value();
554   if (!aEnvStr.IsEmpty())
555   {
556     aValue *= Draw::Atoi (aEnvStr.ToCString());
557   }
558   return aValue;
559 }
560
561 static Standard_Integer cpulimit(Draw_Interpretor& di, Standard_Integer n, const char** a)
562 {
563   static int aFirst = 1;
564 #ifdef _WIN32
565   // Windows specific code
566   unsigned int __stdcall CpuFunc (void *);
567   unsigned aThreadID;
568
569   if (n <= 1){
570     CPU_LIMIT = RLIM_INFINITY;
571   } else {
572     CPU_LIMIT = GetCpuLimit (a[1]);
573     Standard_Real anUserSeconds, aSystemSeconds;
574     OSD_Chronometer::GetProcessCPU (anUserSeconds, aSystemSeconds);
575     CPU_CURRENT = clock_t(anUserSeconds + aSystemSeconds);
576     aTimer.Reset();
577     aTimer.Start();
578     if (aFirst) // Launch the thread only at the 1st call.
579     {
580       aFirst = 0;
581       _beginthreadex (NULL, 0, CpuFunc, NULL, 0, &aThreadID);
582     }
583   }
584
585 #else 
586   // Unix & Linux
587   rlimit rlp;
588   rlp.rlim_max = RLIM_INFINITY;
589   if (n <= 1)
590     rlp.rlim_cur = RLIM_INFINITY;
591   else
592     rlp.rlim_cur = GetCpuLimit (a[1]);
593   CPU_LIMIT = rlp.rlim_cur;
594
595   int status;
596   status=setrlimit(RLIMIT_CPU,&rlp);
597   if (status !=0)
598     di << "status cpulimit setrlimit : " << status << "\n";
599
600   // set signal handler to print a message before death
601   struct sigaction act, oact;
602   memset (&act, 0, sizeof(act));
603   act.sa_handler = cpulimitSignalHandler;
604   sigaction (SIGXCPU, &act, &oact);
605
606   // cpulimit for elapsed time
607   aTimer.Reset();
608   aTimer.Start();
609   pthread_t cpulimitThread;
610   if (aFirst) // Launch the thread only at the 1st call.
611   {
612     aFirst = 0;
613     pthread_create(&cpulimitThread, NULL, CpuFunc, NULL);
614   }
615 #endif
616   di << "CPU and elapsed time limit set to " << (double)CPU_LIMIT << " seconds";
617   return 0;
618 }
619
620 //=======================================================================
621 //function : mallochook
622 //purpose  : 
623 //=======================================================================
624
625 static Standard_Integer mallochook(Draw_Interpretor& di, Standard_Integer n,
626                                    const char** a)
627 {
628   if (n < 2)
629   {
630     di << "\
631 usage: mallochook cmd\n\
632 where cmd is one of:\n\
633   set [<op>]      - set callback to malloc/free; op is one of the following:\n\
634                     0 - set callback to NULL,\n\
635                     1 - set callback OSD_MAllocHook::CollectBySize (default)\n\
636                     2 - set callback OSD_MAllocHook::LogFileHandler\n\
637   reset           - reset the CollectBySize handler\n\
638   report1 [<outfile>]\n\
639                   - write report from CollectBySize handler in <outfile>\n\
640   open [<logfile>]\n\
641                   - open file for writing the log with LogFileHandler\n\
642   close           - close the log file with LogFileHandler\n\
643   report2 [<flag>] [<logfile>] [<outfile>]\n\
644                   - scan <logfile> written with LogFileHandler\n\
645                     and make synthesized report in <outfile>; <flag> can be:\n\
646                     0 - simple stats by sizes (default),\n\
647                     1 - with alive allocation numbers\n\
648 By default <logfile> is \"mem-log.txt\", <outfile> is \"mem-stat.txt\""
649       << "\n";
650     return 0;
651   }
652   if (strcmp(a[1], "set") == 0)
653   {
654     int aType = (n > 2 ? Draw::Atoi(a[2]) : 1);
655     if (aType < 0 || aType > 2)
656     {
657       di << "unknown op of the command set\n";
658       return 1;
659     }
660     else if (aType == 0)
661     {
662       OSD_MAllocHook::SetCallback(NULL);
663       di << "callback is unset\n";
664     }
665     else if (aType == 1)
666     {
667       OSD_MAllocHook::SetCallback(OSD_MAllocHook::GetCollectBySize());
668       di << "callback is set to CollectBySize\n";
669     }
670     else //if (aType == 2)
671     {
672       OSD_MAllocHook::SetCallback(OSD_MAllocHook::GetLogFileHandler());
673       di << "callback is set to LogFileHandler\n";
674     }
675   }
676   else if (strcmp(a[1], "reset") == 0)
677   {
678     OSD_MAllocHook::GetCollectBySize()->Reset();
679     di << "CollectBySize handler is reset\n";
680   }
681   else if (strcmp(a[1], "open") == 0)
682   {
683     const char* aFileName = (n > 2 ? a[2] : "mem-log.txt");
684     if (!OSD_MAllocHook::GetLogFileHandler()->Open(aFileName))
685     {
686       di << "cannot create file " << aFileName << " for writing\n";
687       return 1;
688     }
689     di << "log file " << aFileName << " is opened for writing\n";
690   }
691   else if (strcmp(a[1], "close") == 0)
692   {
693     OSD_MAllocHook::GetLogFileHandler()->Close();
694     di << "log file is closed\n";
695   }
696   else if (strcmp(a[1], "report1") == 0)
697   {
698     const char* aOutFile = "mem-stat.txt";
699     if (n > 2)
700       aOutFile = a[2];
701     if (OSD_MAllocHook::GetCollectBySize()->MakeReport(aOutFile))
702     {
703       di << "report " << aOutFile << " has been created\n";
704     }
705     else
706     {
707       di << "cannot create report " << aOutFile << "\n";
708       return 1;
709     }
710   }
711   else if (strcmp(a[1], "report2") == 0)
712   {
713     Standard_Boolean includeAlive = Standard_False;
714     const char* aLogFile = "mem-log.txt";
715     const char* aOutFile = "mem-stat.txt";
716     if (n > 2)
717     {
718       includeAlive = (Draw::Atoi(a[2]) != 0);
719       if (n > 3)
720       {
721         aLogFile = a[3];
722         if (n > 4)
723           aOutFile = a[4];
724       }
725     }
726     if (OSD_MAllocHook::LogFileHandler::MakeReport(aLogFile, aOutFile, includeAlive))
727     {
728       di << "report " << aOutFile << " has been created\n";
729     }
730     else
731     {
732       di << "cannot create report " << aOutFile << " from the log file "
733         << aLogFile << "\n";
734       return 1;
735     }
736   }
737   else
738   {
739     di << "unrecognized command " << a[1] << "\n";
740     return 1;
741   }
742   return 0;
743 }
744
745 //==============================================================================
746 //function : dlocale
747 //purpose  :
748 //==============================================================================
749
750 static int dlocale (Draw_Interpretor& di, Standard_Integer n, const char** argv)
751 {
752   int category = LC_ALL;
753   if (n > 1)
754   {
755     const char *cat = argv[1];
756     if ( ! strcmp (cat, "LC_ALL") ) category = LC_ALL;
757     else if ( ! strcmp (cat, "LC_COLLATE") ) category = LC_COLLATE;
758     else if ( ! strcmp (cat, "LC_CTYPE") ) category = LC_CTYPE;
759     else if ( ! strcmp (cat, "LC_MONETARY") ) category = LC_MONETARY;
760     else if ( ! strcmp (cat, "LC_NUMERIC") ) category = LC_NUMERIC;
761     else if ( ! strcmp (cat, "LC_TIME") ) category = LC_TIME;
762     else 
763     {
764       std::cout << "Error: cannot recognize argument " << cat << " as one of LC_ macros" << std::endl;
765       return 1;
766     }
767   }
768   const char* locale = (n > 2 ? argv[2] : NULL);
769   const char* result = setlocale (category, locale);
770   if (result)
771     di << result;
772   else 
773     std::cout << "Error: unsupported locale specification: " << locale << std::endl;
774   return 0;
775 }
776
777 //==============================================================================
778 //function : dmeminfo
779 //purpose  :
780 //==============================================================================
781
782 static int dmeminfo (Draw_Interpretor& theDI,
783                      Standard_Integer  theArgNb,
784                      const char**      theArgVec)
785 {
786   if (theArgNb <= 1)
787   {
788     OSD_MemInfo aMemInfo;
789     theDI << aMemInfo.ToString();
790     return 0;
791   }
792
793   NCollection_Map<OSD_MemInfo::Counter> aCounters;
794   for (Standard_Integer anIter = 1; anIter < theArgNb; ++anIter)
795   {
796     TCollection_AsciiString anArg (theArgVec[anIter]);
797     anArg.LowerCase();
798     if (anArg == "virt" || anArg == "v")
799     {
800       aCounters.Add (OSD_MemInfo::MemVirtual);
801     }
802     else if (anArg == "heap" || anArg == "h")
803     {
804       aCounters.Add (OSD_MemInfo::MemHeapUsage);
805     }
806     else if (anArg == "wset" || anArg == "w")
807     {
808       aCounters.Add (OSD_MemInfo::MemWorkingSet);
809     }
810     else if (anArg == "wsetpeak")
811     {
812       aCounters.Add (OSD_MemInfo::MemWorkingSetPeak);
813     }
814     else if (anArg == "swap")
815     {
816       aCounters.Add (OSD_MemInfo::MemSwapUsage);
817     }
818     else if (anArg == "swappeak")
819     {
820       aCounters.Add (OSD_MemInfo::MemSwapUsagePeak);
821     }
822     else if (anArg == "private")
823     {
824       aCounters.Add (OSD_MemInfo::MemPrivate);
825     }
826     else
827     {
828       std::cerr << "Unknown argument '" << theArgVec[anIter] << "'!\n";
829     }
830   }
831
832   OSD_MemInfo aMemInfo (Standard_False);
833   aMemInfo.SetActive (Standard_False);
834   for (NCollection_Map<OSD_MemInfo::Counter>::Iterator aCountersIt (aCounters); aCountersIt.More(); aCountersIt.Next())
835   {
836     aMemInfo.SetActive (aCountersIt.Value(), Standard_True);
837   }
838   aMemInfo.Update();
839
840   for (NCollection_Map<OSD_MemInfo::Counter>::Iterator aCountersIt (aCounters); aCountersIt.More(); aCountersIt.Next())
841   {
842     theDI << Standard_Real (aMemInfo.Value (aCountersIt.Value())) << " ";
843   }
844   theDI << "\n";
845   return 0;
846 }
847
848 //==============================================================================
849 //function : dparallel
850 //purpose  :
851 //==============================================================================
852 static int dparallel (Draw_Interpretor& theDI,
853                       Standard_Integer  theArgNb,
854                       const char**      theArgVec)
855 {
856   const Handle(OSD_ThreadPool)& aDefPool = OSD_ThreadPool::DefaultPool();
857   if (theArgNb <= 1)
858   {
859     theDI << "NbLogicalProcessors: " << OSD_Parallel::NbLogicalProcessors() << "\n"
860           << "NbThreads:           " << aDefPool->NbThreads() << "\n"
861           << "NbDefThreads:        " << aDefPool->NbDefaultThreadsToLaunch() << "\n"
862           << "UseOcct:             " << (OSD_Parallel::ToUseOcctThreads() ? 1 : 0);
863     return 0;
864   }
865
866   for (Standard_Integer anIter = 1; anIter < theArgNb; ++anIter)
867   {
868     TCollection_AsciiString anArg (theArgVec[anIter]);
869     anArg.LowerCase();
870     if (anIter + 1 < theArgNb
871      && (anArg == "-nbthreads"
872       || anArg == "-threads"))
873     {
874       const Standard_Integer aVal = Draw::Atoi (theArgVec[++anIter]);
875       aDefPool->Init (aVal);
876     }
877     else if (anIter + 1 < theArgNb
878           && (anArg == "-nbdefthreads"
879            || anArg == "-defthreads"
880            || anArg == "-nbmaxdefthreads"
881            || anArg == "-maxdefthreads"))
882     {
883       const Standard_Integer aVal = Draw::Atoi (theArgVec[++anIter]);
884       if (aVal <= 0 || aVal > aDefPool->NbThreads())
885       {
886         std::cout << "Syntax error: maximum number of threads to use should be <= of threads in the pool\n";
887         return 1;
888       }
889       aDefPool->SetNbDefaultThreadsToLaunch (aVal);
890     }
891     else if (anIter + 1 < theArgNb
892           && (anArg == "-useocct"
893            || anArg == "-touseocct"
894            || anArg == "-occt"))
895     {
896       const Standard_Integer aVal = Draw::Atoi (theArgVec[++anIter]);
897       OSD_Parallel::SetUseOcctThreads (aVal == 1);
898       if (OSD_Parallel::ToUseOcctThreads() != (aVal == 1))
899       {
900         std::cout << "Warning: unable to switch threads library - no options available\n";
901       }
902     }
903     else if (anIter + 1 < theArgNb
904           && (anArg == "-usetbb"
905            || anArg == "-tousetbb"
906            || anArg == "-tbb"))
907     {
908       const Standard_Integer aVal = Draw::Atoi (theArgVec[++anIter]);
909       OSD_Parallel::SetUseOcctThreads (aVal == 0);
910       if (OSD_Parallel::ToUseOcctThreads() != (aVal == 0))
911       {
912         std::cout << "Warning: unable to switch threads library - no options available\n";
913       }
914     }
915     else
916     {
917       std::cout << "Syntax error: unknown argument '" << anArg << "'\n";
918       return 1;
919     }
920   }
921   return 0;
922 }
923
924 //==============================================================================
925 //function : dperf
926 //purpose  :
927 //==============================================================================
928
929 static int dperf (Draw_Interpretor& theDI, Standard_Integer theArgNb, const char** theArgVec)
930 {
931   // reset if argument is provided and it is not '0'
932   int reset = (theArgNb > 1 ? theArgVec[1][0] != '0' && theArgVec[1][0] != '\0' : 0);
933   char buffer[25600];
934   perf_sprint_all_meters (buffer, 25600 - 1, reset);
935   theDI << buffer;
936   return 0;
937 }
938
939 //==============================================================================
940 //function : dsetsignal
941 //purpose  :
942 //==============================================================================
943
944 static int dsetsignal (Draw_Interpretor& theDI, Standard_Integer theArgNb, const char** theArgVec)
945 {
946   OSD_SignalMode aMode = OSD_SignalMode_Set;
947   Standard_Boolean aSetFPE = OSD::ToCatchFloatingSignals();
948
949   // default for FPE signal is defined by CSF_FPE variable, if set
950   OSD_Environment aEnv("CSF_FPE");
951   TCollection_AsciiString aEnvStr = aEnv.Value();
952   if (!aEnvStr.IsEmpty())
953   {
954     aSetFPE = (aEnvStr.Value(1) != '0');
955   }
956
957   // parse arguments
958   for (Standard_Integer anArgIter = 1; anArgIter < theArgNb; ++anArgIter)
959   {
960     TCollection_AsciiString anArg(theArgVec[anArgIter]);
961     anArg.LowerCase();
962     if (anArg == "asis")
963     {
964       aMode = OSD_SignalMode_AsIs;
965     }
966     else if (anArg == "set")
967     {
968       aMode = OSD_SignalMode_Set;
969     }
970     else if (anArg == "unhandled")
971     {
972       aMode = OSD_SignalMode_SetUnhandled;
973     }
974     else if (anArg == "unset")
975     {
976       aMode = OSD_SignalMode_Unset;
977     }
978     else if (anArg == "1" || anArg == "on")
979     {
980       aSetFPE = Standard_True;
981     }
982     else if (anArg == "0" || anArg == "off")
983     {
984       aSetFPE = Standard_False;
985     }
986     else if (anArg == "default")
987     {
988     }
989     else
990     {
991       std::cout << "Syntax error: unknown argument '" << anArg << "'\n";
992       return 1;
993     }
994   }
995
996   OSD::SetSignal(aMode, aSetFPE);
997
998   // report actual status in the end
999   const char* aModeStr = 0;
1000   switch (OSD::SignalMode())
1001   {
1002   default:
1003   case OSD_SignalMode_AsIs:         aModeStr = "asis";      break;
1004   case OSD_SignalMode_Set:          aModeStr = "set";       break;
1005   case OSD_SignalMode_SetUnhandled: aModeStr = "unhandled"; break;
1006   case OSD_SignalMode_Unset:        aModeStr = "unset";     break;
1007   }
1008   theDI << "Signal mode: " << aModeStr << "\n"
1009         << "Catch FPE: " << (OSD::ToCatchFloatingSignals() ? "1" : "0") << "\n";
1010   return 0;
1011 }
1012
1013 //==============================================================================
1014 //function : dtracelevel
1015 //purpose  :
1016 //==============================================================================
1017
1018 static int dtracelevel (Draw_Interpretor& theDI,
1019                         Standard_Integer  theArgNb,
1020                         const char**      theArgVec)
1021 {
1022   Message_Gravity aLevel = Message_Info;
1023   if (theArgNb < 1 || theArgNb > 2)
1024   {
1025     std::cout << "Error: wrong number of arguments! See usage:\n";
1026     theDI.PrintHelp (theArgVec[0]);
1027     return 1;
1028   }
1029   else if (theArgNb == 2)
1030   {
1031     TCollection_AsciiString aVal (theArgVec[1]);
1032     aVal.LowerCase();
1033     if (aVal == "trace")
1034     {
1035       aLevel = Message_Trace;
1036     }
1037     else if (aVal == "info")
1038     {
1039       aLevel = Message_Info;
1040     }
1041     else if (aVal == "warn"
1042           || aVal == "warning")
1043     {
1044       aLevel = Message_Warning;
1045     }
1046     else if (aVal == "alarm")
1047     {
1048       aLevel = Message_Alarm;
1049     }
1050     else if (aVal == "fail")
1051     {
1052       aLevel = Message_Fail;
1053     }
1054     else
1055     {
1056       std::cout << "Error: unknown gravity '" << theArgVec[1] << "'!\n";
1057       return 1;
1058     }
1059   }
1060
1061   Handle(Message_Messenger) aMessenger = Message::DefaultMessenger();
1062   if (aMessenger.IsNull())
1063   {
1064     std::cout << "Error: default messenger is unavailable!\n";
1065     return 1;
1066   }
1067
1068   Message_SequenceOfPrinters& aPrinters = aMessenger->ChangePrinters();
1069   if (aPrinters.Length() < 1)
1070   {
1071     std::cout << "Error: no printers registered in default Messenger!\n";
1072     return 0;
1073   }
1074
1075   for (Standard_Integer aPrinterIter = 1; aPrinterIter <= aPrinters.Length(); ++aPrinterIter)
1076   {
1077     Handle(Message_Printer)& aPrinter = aPrinters.ChangeValue (aPrinterIter);
1078     if (theArgNb == 1)
1079     {
1080       if (aPrinterIter == 1)
1081       {
1082         aLevel = aPrinter->GetTraceLevel();
1083       }
1084       else if (aLevel == aPrinter->GetTraceLevel())
1085       {
1086         continue;
1087       }
1088
1089       switch (aPrinter->GetTraceLevel())
1090       {
1091         case Message_Trace:   theDI << "trace"; break;
1092         case Message_Info:    theDI << "info";  break;
1093         case Message_Warning: theDI << "warn";  break;
1094         case Message_Alarm:   theDI << "alarm"; break;
1095         case Message_Fail:    theDI << "fail";  break;
1096       }
1097       continue;
1098     }
1099
1100     aPrinter->SetTraceLevel (aLevel);
1101   }
1102
1103   return 0;
1104 }
1105
1106 void Draw::BasicCommands(Draw_Interpretor& theCommands)
1107 {
1108   static Standard_Boolean Done = Standard_False;
1109   if (Done) return;
1110   Done = Standard_True;
1111
1112   std::ios::sync_with_stdio();
1113
1114   const char* g = "DRAW General Commands";
1115
1116   theCommands.Add("batch", "returns 1 in batch mode",
1117                   __FILE__,ifbatch,g);
1118   theCommands.Add("spy","spy [file], Save commands in file. no file close",
1119                   __FILE__,spy,g);
1120   theCommands.Add("wait","wait [time(10)], wait time seconds",
1121                   __FILE__,Draw_wait,g);
1122   theCommands.Add("cpulimit","cpulimit [nbseconds], no args remove limits",
1123                   __FILE__,cpulimit,g);
1124   theCommands.Add("chrono","chrono [name action [action...]] \n  Operates named timer.\n"
1125                            "  Supported actions: reset, start, stop, restart, show, counter [text].\n"
1126                            "  Without arguments enables / disables global timer for all DRAW commands.",
1127                   __FILE__,chronom,g);
1128   theCommands.Add("dchrono","see help of chrono command",
1129                   __FILE__,dchronom,g);
1130   theCommands.Add("mallochook",
1131                   "debug memory allocation/deallocation, w/o args for help",
1132                   __FILE__, mallochook, g);
1133   theCommands.Add ("meminfo",
1134     "meminfo [virt|v] [heap|h] [wset|w] [wsetpeak] [swap] [swappeak] [private]"
1135     " : memory counters for this process",
1136           __FILE__, dmeminfo, g);
1137   theCommands.Add("dperf","dperf [reset] -- show performance counters, reset if argument is provided",
1138                   __FILE__,dperf,g);
1139   theCommands.Add("dsetsignal","dsetsignal [{asis|set|unhandled|unset}=set] [{0|1|default=$CSF_FPE}]\n -- set OSD signal handler, with FPE option if argument is given",
1140                   __FILE__,dsetsignal,g);
1141
1142   theCommands.Add("dparallel",
1143     "dparallel [-occt {0|1}] [-nbThreads Count] [-nbDefThreads Count]"
1144     "\n\t\t: Manages global parallelization parameters:"
1145     "\n\t\t:   -occt         use OCCT implementation or external library (if available)"
1146     "\n\t\t:   -nbThreads    specify the number of threads in default thread pool"
1147     "\n\t\t:   -nbDefThreads specify the upper limit of threads to be used for default thread pool"
1148     "\n\t\t:                 within single parallelization call (should be <= of overall number of threads),"
1149     "\n\t\t:                 so that nested algorithm can also use this pool",
1150       __FILE__,dparallel,g);
1151
1152   // Logging commands; note that their names are hard-coded in the code
1153   // of Draw_Interpretor, thus should not be changed without update of that code!
1154   theCommands.Add("dlog", "manage logging of commands and output; run without args to get help",
1155                   __FILE__,dlog,g);
1156   theCommands.Add("decho", "switch on / off echo of commands to cout; run without args to get help",
1157                   __FILE__,decho,g);
1158   theCommands.Add("dtracelevel", "dtracelevel [trace|info|warn|alarm|fail]",
1159                   __FILE__, dtracelevel, g);
1160
1161   theCommands.Add("dbreak", "raises Tcl exception if user has pressed Control-Break key",
1162                   __FILE__,dbreak,g);
1163   theCommands.Add("dversion", "provides information on OCCT build configuration (version, compiler, OS, C library, etc.)",
1164                   __FILE__,dversion,g);
1165   theCommands.Add("dlocale", "set and / or query locate of C subsystem (function setlocale())",
1166                   __FILE__,dlocale,g);
1167 }