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