xref: /trunk/main/basic/source/comp/sbcomp.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basic.hxx"
30 
31 #include <basic/sbx.hxx>
32 #include "sbcomp.hxx"
33 #include "image.hxx"
34 #include "sbtrace.hxx"
35 #include <basic/sbobjmod.hxx>
36 #include <stdio.h>
37 
38 //==========================================================================
39 // Tracing, for debugging only
40 
41 // To activate tracing enable in sbtrace.hxx
42 #ifdef DBG_TRACE_BASIC
43 
44 #include <hash_map>
45 
46 // Trace ini file (set NULL to ignore)
47 // can be overridden with the environment variable OOO_BASICTRACEINI
48 static char     GpTraceIniFile[] = "~/BasicTrace.ini";
49 //static char*  GpTraceIniFile = NULL;
50 
51 
52 // Trace Settings, used if no ini file / not found in ini file
53 static char     GpTraceFileNameDefault[] = "~/BasicTrace.txt";
54 static char*    GpTraceFileName = GpTraceFileNameDefault;
55 
56 // GbTraceOn:
57 // true = tracing is active, false = tracing is disabled, default = true
58 // Set to false initially if you want to activate tracing on demand with
59 // TraceCommand( "TraceOn" ), see below
60 static bool GbTraceOn = true;
61 
62 // GbIncludePCodes:
63 // true = PCodes are written to trace, default = false, correspondents
64 // with TraceCommand( "PCodeOn" / "PCodeOff" ), see below
65 static bool GbIncludePCodes = false;
66 
67 // GbInitOnlyAtOfficeStart:
68 // true = Tracing is only intialized onces after Office start when
69 // Basic runs the first time. Further calls to Basic, e.g. via events
70 // use the same output file. The trace ini file is not read again.
71 static bool GbInitOnlyAtOfficeStart = false;
72 
73 static int  GnIndentPerCallLevel = 4;
74 static int  GnIndentForPCode = 2;
75 
76 /*
77     With trace enabled the runtime function TraceCommand
78     can be used to influence the trace functionality
79     from within the running Basic macro.
80 
81     Format: TraceCommand( command as String [, param as Variant] )
82 
83     Supported commands (command is NOT case sensitive):
84     TraceCommand "TraceOn"          sets GbTraceOn = true
85     TraceCommand "TraceOff"         sets GbTraceOn = false
86 
87     TraceCommand "PCodeOn"          sets GbIncludePCodes = true
88     TraceCommand "PCodeOff"         sets GbIncludePCodes = false
89 
90     TraceCommand "Print", aVal      writes aVal into the trace file as
91                                     long as it can be converted to string
92 */
93 
94 #ifdef DBG_TRACE_PROFILING
95 
96 #include <algorithm>
97 #include <stack>
98 #include "canvas/elapsedtime.hxx"
99 
100 //*** Profiling ***
101 // GbTimerOn:
102 // true = including time stamps
103 static bool GbTimerOn = true;
104 
105 // GbTimeStampForEachStep:
106 // true = prints time stamp after each command / pcode (very slow)
107 static bool GbTimeStampForEachStep = false;
108 
109 // GbBlockAllAfterFirstFunctionUsage:
110 // true = everything (commands, pcodes, functions) is only printed
111 // for the first usage (improves performance when tracing / pro-
112 // filing large macros)
113 static bool GbBlockAllAfterFirstFunctionUsage = false;
114 
115 // GbBlockStepsAfterFirstFunctionUsage:
116 // true = commands / pcodes are only printed for the first time
117 // a function is executed. Afterwards only the entering/leaving
118 // messages are logged (improves performance when tracing / pro-
119 // filing large macros)
120 static bool GbBlockStepsAfterFirstFunctionUsage = false;
121 
122 #endif
123 
124 
125 static void lcl_skipWhites( char*& rpc )
126 {
127     while( *rpc == ' ' || *rpc == '\t' )
128         ++rpc;
129 }
130 
131 inline void lcl_findNextLine( char*& rpc, char* pe )
132 {
133     // Find line end
134     while( rpc < pe && *rpc != 13 && *rpc != 10 )
135         ++rpc;
136 
137     // Read all
138     while( rpc < pe && (*rpc == 13 || *rpc == 10) )
139         ++rpc;
140 }
141 
142 inline bool lcl_isAlpha( char c )
143 {
144     bool bRet = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
145     return bRet;
146 }
147 
148 static void lcl_ReadIniFile( const char* pIniFileName )
149 {
150     const int BUF_SIZE = 1000;
151     static sal_Char TraceFileNameBuffer[BUF_SIZE];
152     sal_Char Buffer[BUF_SIZE];
153     sal_Char VarNameBuffer[BUF_SIZE];
154     sal_Char ValBuffer[BUF_SIZE];
155 
156     FILE* pFile = fopen( pIniFileName ,"rb" );
157     if( pFile == NULL )
158         return;
159 
160     size_t nRead = fread( Buffer, 1, BUF_SIZE, pFile );
161 
162     // Scan
163     char* pc = Buffer;
164     char* pe = Buffer + nRead;
165     while( pc < pe )
166     {
167         lcl_skipWhites( pc ); if( pc == pe ) break;
168 
169         // Read variable
170         char* pVarStart = pc;
171         while( pc < pe && lcl_isAlpha( *pc ) )
172             ++pc;
173         int nVarLen = pc - pVarStart;
174         if( nVarLen == 0 )
175         {
176             lcl_findNextLine( pc, pe );
177             continue;
178         }
179         strncpy( VarNameBuffer, pVarStart, nVarLen );
180         VarNameBuffer[nVarLen] = '\0';
181 
182         // Check =
183         lcl_skipWhites( pc ); if( pc == pe ) break;
184         if( *pc != '=' )
185             continue;
186         ++pc;
187         lcl_skipWhites( pc ); if( pc == pe ) break;
188 
189         // Read value
190         char* pValStart = pc;
191         while( pc < pe && *pc != 13 && *pc != 10 )
192             ++pc;
193         int nValLen = pc - pValStart;
194         if( nValLen == 0 )
195         {
196             lcl_findNextLine( pc, pe );
197             continue;
198         }
199         strncpy( ValBuffer, pValStart, nValLen );
200         ValBuffer[nValLen] = '\0';
201 
202         // Match variables
203         if( strcmp( VarNameBuffer, "GpTraceFileName") == 0 )
204         {
205             strcpy( TraceFileNameBuffer, ValBuffer );
206             GpTraceFileName = TraceFileNameBuffer;
207         }
208         else
209         if( strcmp( VarNameBuffer, "GbTraceOn") == 0 )
210             GbTraceOn = (strcmp( ValBuffer, "true" ) == 0);
211         else
212         if( strcmp( VarNameBuffer, "GbIncludePCodes") == 0 )
213             GbIncludePCodes = (strcmp( ValBuffer, "true" ) == 0);
214         else
215         if( strcmp( VarNameBuffer, "GbInitOnlyAtOfficeStart") == 0 )
216             GbInitOnlyAtOfficeStart = (strcmp( ValBuffer, "true" ) == 0);
217         else
218         if( strcmp( VarNameBuffer, "GnIndentPerCallLevel") == 0 )
219             GnIndentPerCallLevel = strtol( ValBuffer, NULL, 10 );
220         else
221         if( strcmp( VarNameBuffer, "GnIndentForPCode") == 0 )
222             GnIndentForPCode = strtol( ValBuffer, NULL, 10 );
223 #ifdef DBG_TRACE_PROFILING
224         else
225         if( strcmp( VarNameBuffer, "GbTimerOn") == 0 )
226             GbTimerOn = (strcmp( ValBuffer, "true" ) == 0);
227         else
228         if( strcmp( VarNameBuffer, "GbTimeStampForEachStep") == 0 )
229             GbTimeStampForEachStep = (strcmp( ValBuffer, "true" ) == 0);
230         else
231         if( strcmp( VarNameBuffer, "GbBlockAllAfterFirstFunctionUsage") == 0 )
232             GbBlockAllAfterFirstFunctionUsage = (strcmp( ValBuffer, "true" ) == 0);
233         else
234         if( strcmp( VarNameBuffer, "GbBlockStepsAfterFirstFunctionUsage") == 0 )
235             GbBlockStepsAfterFirstFunctionUsage = (strcmp( ValBuffer, "true" ) == 0);
236 #endif
237     }
238     fclose( pFile );
239 }
240 
241 struct TraceTextData
242 {
243     rtl::OString m_aTraceStr_STMNT;
244     rtl::OString m_aTraceStr_PCode;
245 };
246 typedef std::hash_map< sal_Int32, TraceTextData > PCToTextDataMap;
247 typedef std::hash_map< ::rtl::OUString, PCToTextDataMap*, ::rtl::OUStringHash, ::std::equal_to< ::rtl::OUString > > ModuleTraceMap;
248 
249 ModuleTraceMap      GaModuleTraceMap;
250 ModuleTraceMap&     rModuleTraceMap = GaModuleTraceMap;
251 
252 static void lcl_PrepareTraceForModule( SbModule* pModule )
253 {
254     String aModuleName = pModule->GetName();
255     ModuleTraceMap::iterator it = rModuleTraceMap.find( aModuleName );
256     if( it != rModuleTraceMap.end() )
257     {
258         PCToTextDataMap* pInnerMap = it->second;
259         delete pInnerMap;
260         rModuleTraceMap.erase( it );
261     }
262 
263     String aDisassemblyStr;
264     pModule->Disassemble( aDisassemblyStr );
265 }
266 
267 static FILE* GpGlobalFile = NULL;
268 
269 static void lcl_lineOut( const char* pStr, const char* pPreStr = NULL, const char* pPostStr = NULL )
270 {
271     if( GpGlobalFile != NULL )
272     {
273         fprintf( GpGlobalFile, "%s%s%s\n", pPreStr ? pPreStr : "", pStr, pPostStr ? pPostStr : "" );
274         fflush( GpGlobalFile );
275     }
276 }
277 
278 const char* lcl_getSpaces( int nSpaceCount )
279 {
280     static sal_Char Spaces[] = "                                                                                                    "
281         "                                                                                                    "
282         "                                                                                                    ";
283     static int nAvailableSpaceCount = strlen( Spaces );
284     static sal_Char* pSpacesEnd = Spaces + nAvailableSpaceCount;
285 
286     if( nSpaceCount > nAvailableSpaceCount )
287         nSpaceCount = nAvailableSpaceCount;
288 
289     return pSpacesEnd - nSpaceCount;
290 }
291 
292 static rtl::OString lcl_toOStringSkipLeadingWhites( const String& aStr )
293 {
294     static sal_Char Buffer[1000];
295 
296     rtl::OString aOStr = OUStringToOString( rtl::OUString( aStr ), RTL_TEXTENCODING_ASCII_US );
297     const sal_Char* pStr = aOStr.getStr();
298 
299     // Skip whitespace
300     sal_Char c = *pStr;
301     while( c == ' ' || c == '\t' )
302     {
303         pStr++;
304         c = *pStr;
305     }
306 
307     int nLen = strlen( pStr );
308     strncpy( Buffer, pStr, nLen );
309     Buffer[nLen] = 0;
310 
311     rtl::OString aORetStr( Buffer );
312     return aORetStr;
313 }
314 
315 String lcl_dumpMethodParameters( SbMethod* pMethod )
316 {
317     String aStr;
318     if( pMethod == NULL )
319         return aStr;
320 
321     SbxError eOld = SbxBase::GetError();
322 
323     SbxArray* pParams = pMethod->GetParameters();
324     SbxInfo* pInfo = pMethod->GetInfo();
325     if ( pParams )
326     {
327         aStr += '(';
328         // 0 is sub itself
329         for ( sal_uInt16 nParam = 1; nParam < pParams->Count(); nParam++ )
330         {
331             SbxVariable* pVar = pParams->Get( nParam );
332             DBG_ASSERT( pVar, "Parameter?!" );
333             if ( pVar->GetName().Len() )
334                 aStr += pVar->GetName();
335             else if ( pInfo )
336             {
337                 const SbxParamInfo* pParam = pInfo->GetParam( nParam );
338                 if ( pParam )
339                     aStr += pParam->aName;
340             }
341             aStr += '=';
342             SbxDataType eType = pVar->GetType();
343             if( eType & SbxARRAY )
344                 aStr += String( RTL_CONSTASCII_USTRINGPARAM( "..." ) );
345             else if( eType != SbxOBJECT )
346                 aStr += pVar->GetString();
347             if ( nParam < ( pParams->Count() - 1 ) )
348                 aStr += String( RTL_CONSTASCII_USTRINGPARAM( ", " ) );
349         }
350         aStr += ')';
351     }
352 
353     SbxBase::ResetError();
354     if( eOld != SbxERR_OK )
355         SbxBase::SetError( eOld );
356 
357     return aStr;
358 }
359 
360 
361 // Public functions
362 static bool GbSavTraceOn = false;
363 
364 #ifdef DBG_TRACE_PROFILING
365 static canvas::tools::ElapsedTime* GpTimer = NULL;
366 static double GdStartTime = 0.0;
367 static double GdLastTime = 0.0;
368 static bool GbBlockSteps = false;
369 static bool GbBlockAll = false;
370 
371 struct FunctionItem
372 {
373     String      m_aCompleteFunctionName;
374     double      m_dTotalTime;
375     double      m_dNetTime;
376     int         m_nCallCount;
377     bool        m_bBlockAll;
378     bool        m_bBlockSteps;
379 
380     FunctionItem( void )
381         : m_dTotalTime( 0.0 )
382         , m_dNetTime( 0.0 )
383         , m_nCallCount( 0 )
384         , m_bBlockAll( false )
385         , m_bBlockSteps( false )
386     {}
387 };
388 typedef std::hash_map< ::rtl::OUString, FunctionItem*, ::rtl::OUStringHash, ::std::equal_to< ::rtl::OUString > > FunctionItemMap;
389 
390 static std::stack< double >             GaCallEnterTimeStack;
391 static std::stack< FunctionItem* >      GaFunctionItemStack;
392 static FunctionItemMap                  GaFunctionItemMap;
393 
394 bool compareFunctionNetTime( FunctionItem* p1, FunctionItem* p2 )
395 {
396     return (p1->m_dNetTime > p2->m_dNetTime);
397 }
398 
399 void lcl_printTimeOutput( void )
400 {
401     // Overall time output
402     lcl_lineOut( "" );
403     lcl_lineOut( "***** Time Output *****" );
404     char TimeBuffer[500];
405     double dTotalTime = GpTimer->getElapsedTime() - GdStartTime;
406     sprintf( TimeBuffer, "Total execution time = %f ms", dTotalTime*1000.0 );
407     lcl_lineOut( TimeBuffer );
408     lcl_lineOut( "" );
409 
410     if( GbTimerOn )
411     {
412         lcl_lineOut( "Functions:" );
413 
414         std::vector<FunctionItem*> avFunctionItems;
415 
416         FunctionItemMap::iterator it;
417         for( it = GaFunctionItemMap.begin() ; it != GaFunctionItemMap.end() ; ++it )
418         {
419             FunctionItem* pFunctionItem = it->second;
420             if( pFunctionItem != NULL )
421                 avFunctionItems.push_back( pFunctionItem );
422         }
423 
424         std::sort( avFunctionItems.begin(), avFunctionItems.end(), compareFunctionNetTime );
425 
426         std::vector<FunctionItem*>::iterator itv;
427         for( itv = avFunctionItems.begin() ; itv != avFunctionItems.end() ; ++itv )
428         {
429             FunctionItem* pFunctionItem = *itv;
430             if( pFunctionItem != NULL )
431             {
432                 rtl::OUString aCompleteFunctionName = pFunctionItem->m_aCompleteFunctionName;
433                 const char* pName = OUStringToOString( aCompleteFunctionName, RTL_TEXTENCODING_ASCII_US ).getStr();
434                 int nNameLen = aCompleteFunctionName.getLength();
435 
436                 double dFctTotalTime = pFunctionItem->m_dTotalTime;
437                 double dFctNetTime = pFunctionItem->m_dNetTime;
438                 double dFctTotalTimePercent = 100.0 * dFctTotalTime / dTotalTime;
439                 double dFctNetTimePercent = 100.0 * dFctNetTime / dTotalTime;
440                 int nSpaceCount = 30 - nNameLen;
441                 if( nSpaceCount < 0 )
442                     nSpaceCount = 2;
443                 sprintf( TimeBuffer, "%s:%sCalled %d times\t%f ms (%f%%) / net %f (%f%%) ms",
444                     pName, lcl_getSpaces( nSpaceCount ), pFunctionItem->m_nCallCount,
445                     dFctTotalTime*1000.0, dFctTotalTimePercent, dFctNetTime*1000.0, dFctNetTimePercent );
446                 lcl_lineOut( TimeBuffer );
447             }
448         }
449     }
450 }
451 #endif
452 
453 
454 static bool GbInitTraceAlreadyCalled = false;
455 
456 void dbg_InitTrace( void )
457 {
458     if( GbInitOnlyAtOfficeStart && GbInitTraceAlreadyCalled )
459     {
460 #ifdef DBG_TRACE_PROFILING
461         if( GbTimerOn )
462             GpTimer->continueTimer();
463 #endif
464         GpGlobalFile = fopen( GpTraceFileName, "a+" );
465         return;
466     }
467     GbInitTraceAlreadyCalled = true;
468 
469     if( const sal_Char* pcIniFileName = ::getenv( "OOO_BASICTRACEINI" ) )
470         lcl_ReadIniFile( pcIniFileName );
471     else if( GpTraceIniFile != NULL )
472         lcl_ReadIniFile( GpTraceIniFile );
473 
474     GpGlobalFile = fopen( GpTraceFileName, "w" );
475     GbSavTraceOn = GbTraceOn;
476     if( !GbTraceOn )
477         lcl_lineOut( "### Program started with trace off ###" );
478 
479 #ifdef DBG_TRACE_PROFILING
480     GpTimer = new canvas::tools::ElapsedTime();
481     GdStartTime = GpTimer->getElapsedTime();
482     GdLastTime = GdStartTime;
483     GbBlockSteps = false;
484     GbBlockAll = false;
485 #endif
486 }
487 
488 void dbg_DeInitTrace( void )
489 {
490     GbTraceOn = GbSavTraceOn;
491 
492 #ifdef DBG_TRACE_PROFILING
493     while( !GaCallEnterTimeStack.empty() )
494         GaCallEnterTimeStack.pop();
495     while( !GaFunctionItemStack.empty() )
496         GaFunctionItemStack.pop();
497 
498     lcl_printTimeOutput();
499 
500     for( FunctionItemMap::iterator it = GaFunctionItemMap.begin() ; it != GaFunctionItemMap.end() ; ++it )
501         delete it->second;
502     GaFunctionItemMap.clear();
503 
504     if( GpGlobalFile )
505     {
506         fclose( GpGlobalFile );
507         GpGlobalFile = NULL;
508     }
509 
510     if( GbInitOnlyAtOfficeStart )
511     {
512         if( GbTimerOn )
513             GpTimer->pauseTimer();
514     }
515     else
516     {
517         delete GpTimer;
518     }
519 #endif
520 }
521 
522 static sal_Int32 GnLastCallLvl = 0;
523 
524 void dbg_tracePrint( const String& aStr, sal_Int32 nCallLvl, bool bCallLvlRelativeToCurrent )
525 {
526     if( bCallLvlRelativeToCurrent )
527         nCallLvl += GnLastCallLvl;
528 
529     int nIndent = nCallLvl * GnIndentPerCallLevel;
530     lcl_lineOut( OUStringToOString( rtl::OUString( aStr ), RTL_TEXTENCODING_ASCII_US ).getStr(), lcl_getSpaces( nIndent ) );
531 }
532 
533 void dbg_traceStep( SbModule* pModule, sal_uInt32 nPC, sal_Int32 nCallLvl )
534 {
535     if( !GbTraceOn )
536         return;
537 
538 #ifdef DBG_TRACE_PROFILING
539     if( GbBlockSteps || GbBlockAll )
540         return;
541 
542     double dCurTime = 0.0;
543     bool bPrintTimeStamp = false;
544     if( GbTimerOn )
545     {
546         GpTimer->pauseTimer();
547         dCurTime = GpTimer->getElapsedTime();
548         bPrintTimeStamp = GbTimeStampForEachStep;
549     }
550 #else
551     bool bPrintTimeStamp = false;
552 #endif
553 
554     GnLastCallLvl = nCallLvl;
555 
556     SbModule* pTraceMod = pModule;
557     if( pTraceMod->ISA(SbClassModuleObject) )
558     {
559         SbClassModuleObject* pClassModuleObj = (SbClassModuleObject*)(SbxBase*)pTraceMod;
560         pTraceMod = pClassModuleObj->getClassModule();
561     }
562 
563     String aModuleName = pTraceMod->GetName();
564     ModuleTraceMap::iterator it = rModuleTraceMap.find( aModuleName );
565     if( it == rModuleTraceMap.end() )
566     {
567         const char* pModuleNameStr = OUStringToOString( rtl::OUString( aModuleName ), RTL_TEXTENCODING_ASCII_US ).getStr();
568         char Buffer[200];
569         sprintf( Buffer, "TRACE ERROR: Unknown module \"%s\"", pModuleNameStr );
570         lcl_lineOut( Buffer );
571         return;
572     }
573 
574     PCToTextDataMap* pInnerMap = it->second;
575     if( pInnerMap == NULL )
576     {
577         lcl_lineOut( "TRACE INTERNAL ERROR: No inner map" );
578         return;
579     }
580 
581     PCToTextDataMap::iterator itInner = pInnerMap->find( nPC );
582     if( itInner == pInnerMap->end() )
583     {
584         const char* pModuleNameStr = OUStringToOString( rtl::OUString( aModuleName ), RTL_TEXTENCODING_ASCII_US ).getStr();
585         char Buffer[200];
586         sprintf( Buffer, "TRACE ERROR: No info for PC = %d in module \"%s\"", (int)nPC, pModuleNameStr );
587         lcl_lineOut( Buffer );
588         return;
589     }
590 
591     int nIndent = nCallLvl * GnIndentPerCallLevel;
592 
593     const TraceTextData& rTraceTextData = itInner->second;
594     const rtl::OString& rStr_STMNT = rTraceTextData.m_aTraceStr_STMNT;
595     bool bSTMT = false;
596     if( rStr_STMNT.getLength() )
597         bSTMT = true;
598 
599     char TimeBuffer[200];
600 #ifdef DBG_TRACE_PROFILING
601     if( bPrintTimeStamp )
602     {
603         double dDiffTime = dCurTime - GdLastTime;
604         GdLastTime = dCurTime;
605         sprintf( TimeBuffer, "\t\t// Time = %f ms / += %f ms", dCurTime*1000.0, dDiffTime*1000.0 );
606     }
607 #endif
608 
609     if( bSTMT )
610     {
611         lcl_lineOut( rStr_STMNT.getStr(), lcl_getSpaces( nIndent ),
612             (bPrintTimeStamp && !GbIncludePCodes) ? TimeBuffer : NULL );
613     }
614 
615     if( !GbIncludePCodes )
616     {
617 #ifdef DBG_TRACE_PROFILING
618         if( GbTimerOn )
619             GpTimer->continueTimer();
620 #endif
621         return;
622     }
623 
624     nIndent += GnIndentForPCode;
625     const rtl::OString& rStr_PCode = rTraceTextData.m_aTraceStr_PCode;
626     if( rStr_PCode.getLength() )
627     {
628         lcl_lineOut( rStr_PCode.getStr(), lcl_getSpaces( nIndent ),
629             bPrintTimeStamp ? TimeBuffer : NULL );
630     }
631 
632 #ifdef DBG_TRACE_PROFILING
633     if( GbTimerOn )
634         GpTimer->continueTimer();
635 #endif
636 }
637 
638 
639 void dbg_traceNotifyCall( SbModule* pModule, SbMethod* pMethod, sal_Int32 nCallLvl, bool bLeave )
640 {
641     static const char* pSeparator = "' ================================================================================";
642 
643     if( !GbTraceOn )
644         return;
645 
646 #ifdef DBG_TRACE_PROFILING
647     double dCurTime = 0.0;
648     double dExecutionTime = 0.0;
649     if( GbTimerOn )
650     {
651         dCurTime = GpTimer->getElapsedTime();
652         GpTimer->pauseTimer();
653     }
654 #endif
655 
656     GnLastCallLvl = nCallLvl;
657 
658     SbModule* pTraceMod = pModule;
659     SbClassModuleObject* pClassModuleObj = NULL;
660     if( pTraceMod->ISA(SbClassModuleObject) )
661     {
662         pClassModuleObj = (SbClassModuleObject*)(SbxBase*)pTraceMod;
663         pTraceMod = pClassModuleObj->getClassModule();
664     }
665 
666     String aCompleteFunctionName = pTraceMod->GetName();
667     if( pMethod != NULL )
668     {
669         aCompleteFunctionName.AppendAscii( "::" );
670         String aMethodName = pMethod->GetName();
671         aCompleteFunctionName += aMethodName;
672     }
673     else
674     {
675         aCompleteFunctionName.AppendAscii( "/RunInit" );
676     }
677 
678     bool bOwnBlockSteps = false;
679 #ifdef DBG_TRACE_PROFILING
680     bool bOwnBlockAll = false;
681     FunctionItem* pFunctionItem = NULL;
682     if( GbTimerOn )
683     {
684         FunctionItemMap::iterator itFunctionItem = GaFunctionItemMap.find( aCompleteFunctionName );
685         if( itFunctionItem != GaFunctionItemMap.end() )
686             pFunctionItem = itFunctionItem->second;
687 
688         if( pFunctionItem == NULL )
689         {
690             DBG_ASSERT( !bLeave, "No FunctionItem in leave!" );
691 
692             pFunctionItem = new FunctionItem();
693             pFunctionItem->m_aCompleteFunctionName = aCompleteFunctionName;
694             GaFunctionItemMap[ aCompleteFunctionName ] = pFunctionItem;
695         }
696         else if( GbBlockAllAfterFirstFunctionUsage && !bLeave )
697         {
698             pFunctionItem->m_bBlockAll = true;
699         }
700         else if( GbBlockStepsAfterFirstFunctionUsage && !bLeave )
701         {
702             pFunctionItem->m_bBlockSteps = true;
703         }
704 
705         if( bLeave )
706         {
707             bOwnBlockAll = GbBlockAll;
708             bOwnBlockSteps = GbBlockSteps;
709             GbBlockAll = false;
710             GbBlockSteps = false;
711 
712             dExecutionTime = dCurTime - GaCallEnterTimeStack.top();
713             GaCallEnterTimeStack.pop();
714 
715             pFunctionItem->m_dTotalTime += dExecutionTime;
716             pFunctionItem->m_dNetTime += dExecutionTime;
717             pFunctionItem->m_nCallCount++;
718 
719             GaFunctionItemStack.pop();
720             if( !GaFunctionItemStack.empty() )
721             {
722                 FunctionItem* pParentItem = GaFunctionItemStack.top();
723                 if( pParentItem != NULL )
724                 {
725                     pParentItem->m_dNetTime -= dExecutionTime;
726 
727                     GbBlockSteps = pParentItem->m_bBlockSteps;
728                     GbBlockAll = pParentItem->m_bBlockAll;
729                 }
730             }
731         }
732         else
733         {
734             GbBlockSteps = bOwnBlockSteps = pFunctionItem->m_bBlockSteps;
735             GbBlockAll = bOwnBlockAll = pFunctionItem->m_bBlockAll;
736 
737             GaCallEnterTimeStack.push( dCurTime );
738             GaFunctionItemStack.push( pFunctionItem );
739         }
740     }
741 
742     if( bOwnBlockAll )
743     {
744         if( GbTimerOn )
745             GpTimer->continueTimer();
746         return;
747     }
748 #endif
749 
750     if( nCallLvl > 0 )
751         nCallLvl--;
752     int nIndent = nCallLvl * GnIndentPerCallLevel;
753     if( !bLeave && !bOwnBlockSteps )
754     {
755         lcl_lineOut( "" );
756         lcl_lineOut( pSeparator, lcl_getSpaces( nIndent ) );
757     }
758 
759     String aStr;
760     if( bLeave )
761     {
762         if( !bOwnBlockSteps )
763         {
764             lcl_lineOut( "}", lcl_getSpaces( nIndent ) );
765             aStr.AppendAscii( "' Leaving " );
766         }
767     }
768     else
769     {
770         aStr.AppendAscii( "Entering " );
771     }
772     if( !bLeave || !bOwnBlockSteps )
773         aStr += aCompleteFunctionName;
774 
775     if( !bOwnBlockSteps && pClassModuleObj != NULL )
776     {
777         aStr.AppendAscii( "[this=" );
778         aStr += pClassModuleObj->GetName();
779         aStr.AppendAscii( "]" );
780     }
781     if( !bLeave )
782         aStr += lcl_dumpMethodParameters( pMethod );
783 
784     const char* pPostStr = NULL;
785 #ifdef DBG_TRACE_PROFILING
786     char TimeBuffer[200];
787     if( GbTimerOn && bLeave )
788     {
789         sprintf( TimeBuffer, "    // Execution Time = %f ms", dExecutionTime*1000.0 );
790         pPostStr = TimeBuffer;
791     }
792 #endif
793     lcl_lineOut( (!bLeave || !bOwnBlockSteps) ? OUStringToOString( rtl::OUString( aStr ), RTL_TEXTENCODING_ASCII_US ).getStr() : "}",
794         lcl_getSpaces( nIndent ), pPostStr );
795     if( !bLeave )
796         lcl_lineOut( "{", lcl_getSpaces( nIndent ) );
797 
798     if( bLeave && !bOwnBlockSteps )
799         lcl_lineOut( "" );
800 
801 #ifdef DBG_TRACE_PROFILING
802     if( GbTimerOn )
803         GpTimer->continueTimer();
804 #endif
805 }
806 
807 void dbg_traceNotifyError( SbError nTraceErr, const String& aTraceErrMsg, bool bTraceErrHandled, sal_Int32 nCallLvl )
808 {
809     if( !GbTraceOn )
810         return;
811 #ifdef DBG_TRACE_PROFILING
812     if( GbBlockSteps || GbBlockAll )
813         return;
814 #endif
815     GnLastCallLvl = nCallLvl;
816 
817     rtl::OString aOTraceErrMsg = OUStringToOString( rtl::OUString( aTraceErrMsg ), RTL_TEXTENCODING_ASCII_US );
818 
819     char Buffer[200];
820     const char* pHandledStr = bTraceErrHandled ? " / HANDLED" : "";
821     sprintf( Buffer, "*** ERROR%s, Id = %d, Msg = \"%s\" ***", pHandledStr, (int)nTraceErr, aOTraceErrMsg.getStr() );
822     int nIndent = nCallLvl * GnIndentPerCallLevel;
823     lcl_lineOut( Buffer, lcl_getSpaces( nIndent ) );
824 }
825 
826 void dbg_RegisterTraceTextForPC( SbModule* pModule, sal_uInt32 nPC,
827     const String& aTraceStr_STMNT, const String& aTraceStr_PCode )
828 {
829     String aModuleName = pModule->GetName();
830     ModuleTraceMap::iterator it = rModuleTraceMap.find( aModuleName );
831     PCToTextDataMap* pInnerMap;
832     if( it == rModuleTraceMap.end() )
833     {
834         pInnerMap = new PCToTextDataMap();
835         rModuleTraceMap[ aModuleName ] = pInnerMap;
836     }
837     else
838     {
839         pInnerMap = it->second;
840     }
841 
842     TraceTextData aData;
843 
844     rtl::OString aOTraceStr_STMNT = lcl_toOStringSkipLeadingWhites( aTraceStr_STMNT );
845     aData.m_aTraceStr_STMNT = aOTraceStr_STMNT;
846 
847     rtl::OString aOTraceStr_PCode = lcl_toOStringSkipLeadingWhites( aTraceStr_PCode );
848     aData.m_aTraceStr_PCode = aOTraceStr_PCode;
849 
850     (*pInnerMap)[nPC] = aData;
851 }
852 
853 void RTL_Impl_TraceCommand( StarBASIC* pBasic, SbxArray& rPar, sal_Bool bWrite )
854 {
855     (void)pBasic;
856     (void)bWrite;
857 
858     if ( rPar.Count() < 2 )
859     {
860         StarBASIC::Error( SbERR_BAD_ARGUMENT );
861         return;
862     }
863 
864     String aCommand = rPar.Get(1)->GetString();
865 
866     if( aCommand.EqualsIgnoreCaseAscii( "TraceOn" ) )
867         GbTraceOn = true;
868     else
869     if( aCommand.EqualsIgnoreCaseAscii( "TraceOff" ) )
870         GbTraceOn = false;
871     else
872     if( aCommand.EqualsIgnoreCaseAscii( "PCodeOn" ) )
873         GbIncludePCodes = true;
874     else
875     if( aCommand.EqualsIgnoreCaseAscii( "PCodeOff" ) )
876         GbIncludePCodes = false;
877     else
878     if( aCommand.EqualsIgnoreCaseAscii( "Print" ) )
879     {
880         if ( rPar.Count() < 3 )
881         {
882             StarBASIC::Error( SbERR_BAD_ARGUMENT );
883             return;
884         }
885 
886         SbxError eOld = SbxBase::GetError();
887         if( eOld != SbxERR_OK )
888             SbxBase::ResetError();
889 
890         String aValStr = rPar.Get(2)->GetString();
891         SbxError eErr = SbxBase::GetError();
892         if( eErr != SbxERR_OK )
893         {
894             aValStr = String( RTL_CONSTASCII_USTRINGPARAM( "<ERROR converting value to String>" ) );
895             SbxBase::ResetError();
896         }
897 
898         char Buffer[500];
899         const char* pValStr = OUStringToOString( rtl::OUString( aValStr ), RTL_TEXTENCODING_ASCII_US ).getStr();
900 
901         sprintf( Buffer, "### TRACE_PRINT: %s ###", pValStr );
902         int nIndent = GnLastCallLvl * GnIndentPerCallLevel;
903         lcl_lineOut( Buffer, lcl_getSpaces( nIndent ) );
904 
905         if( eOld != SbxERR_OK )
906             SbxBase::SetError( eOld );
907     }
908 }
909 
910 #endif
911 
912 
913 //==========================================================================
914 // For debugging only
915 //#define DBG_SAVE_DISASSEMBLY
916 
917 #ifdef DBG_SAVE_DISASSEMBLY
918 static bool dbg_bDisassemble = true;
919 #include <comphelper/processfactory.hxx>
920 
921 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
922 #include <com/sun/star/ucb/XSimpleFileAccess3.hpp>
923 #include <com/sun/star/io/XTextOutputStream.hpp>
924 #include <com/sun/star/io/XActiveDataSource.hpp>
925 
926 using namespace comphelper;
927 using namespace rtl;
928 using namespace com::sun::star::uno;
929 using namespace com::sun::star::lang;
930 using namespace com::sun::star::ucb;
931 using namespace com::sun::star::io;
932 
933 void dbg_SaveDisassembly( SbModule* pModule )
934 {
935     bool bDisassemble = dbg_bDisassemble;
936     if( bDisassemble )
937     {
938         Reference< XSimpleFileAccess3 > xSFI;
939         Reference< XTextOutputStream > xTextOut;
940         Reference< XOutputStream > xOut;
941         Reference< XMultiServiceFactory > xSMgr = getProcessServiceFactory();
942         if( xSMgr.is() )
943         {
944             Reference< XSimpleFileAccess3 > xSFI = Reference< XSimpleFileAccess3 >( xSMgr->createInstance
945                 ( OUString::createFromAscii( "com.sun.star.ucb.SimpleFileAccess" ) ), UNO_QUERY );
946             if( xSFI.is() )
947             {
948                 String aFile( RTL_CONSTASCII_USTRINGPARAM("file:///d:/zBasic.Asm/Asm_") );
949                 StarBASIC* pBasic = (StarBASIC*)pModule->GetParent();
950                 if( pBasic )
951                 {
952                     aFile += pBasic->GetName();
953                     aFile.AppendAscii( "_" );
954                 }
955                 aFile += pModule->GetName();
956                 aFile.AppendAscii( ".txt" );
957 
958                 // String aFile( RTL_CONSTASCII_USTRINGPARAM("file:///d:/BasicAsm.txt") );
959                 if( xSFI->exists( aFile ) )
960                     xSFI->kill( aFile );
961                 xOut = xSFI->openFileWrite( aFile );
962                 Reference< XInterface > x = xSMgr->createInstance( OUString::createFromAscii( "com.sun.star.io.TextOutputStream" ) );
963                 Reference< XActiveDataSource > xADS( x, UNO_QUERY );
964                 xADS->setOutputStream( xOut );
965                 xTextOut = Reference< XTextOutputStream >( x, UNO_QUERY );
966             }
967         }
968 
969         if( xTextOut.is() )
970         {
971             String aDisassemblyStr;
972             pModule->Disassemble( aDisassemblyStr );
973             xTextOut->writeString( aDisassemblyStr );
974         }
975         xOut->closeOutput();
976     }
977 }
978 #endif
979 
980 
981 // Diese Routine ist hier definiert, damit der Compiler als eigenes Segment
982 // geladen werden kann.
983 
984 sal_Bool SbModule::Compile()
985 {
986     if( pImage )
987         return sal_True;
988     StarBASIC* pBasic = PTR_CAST(StarBASIC,GetParent());
989     if( !pBasic )
990         return sal_False;
991     SbxBase::ResetError();
992     // Aktuelles Modul!
993     SbModule* pOld = pCMOD;
994     pCMOD = this;
995 
996     SbiParser* pParser = new SbiParser( (StarBASIC*) GetParent(), this );
997     while( pParser->Parse() ) {}
998     if( !pParser->GetErrors() )
999         pParser->aGen.Save();
1000     delete pParser;
1001     // fuer den Disassembler
1002     if( pImage )
1003         pImage->aOUSource = aOUSource;
1004 
1005     pCMOD = pOld;
1006 
1007     // Beim Compilieren eines Moduls werden die Modul-globalen
1008     // Variablen aller Module ungueltig
1009     sal_Bool bRet = IsCompiled();
1010     if( bRet )
1011     {
1012         if( !this->ISA(SbObjModule) )
1013             pBasic->ClearAllModuleVars();
1014         RemoveVars(); // remove 'this' Modules variables
1015         // clear all method statics
1016         for( sal_uInt16 i = 0; i < pMethods->Count(); i++ )
1017         {
1018             SbMethod* p = PTR_CAST(SbMethod,pMethods->Get( i ) );
1019             if( p )
1020                 p->ClearStatics();
1021         }
1022 
1023         // #i31510 Init other libs only if Basic isn't running
1024         if( pINST == NULL )
1025         {
1026             SbxObject* pParent_ = pBasic->GetParent();
1027             if( pParent_ )
1028                 pBasic = PTR_CAST(StarBASIC,pParent_);
1029             if( pBasic )
1030                 pBasic->ClearAllModuleVars();
1031         }
1032     }
1033 
1034 #ifdef DBG_SAVE_DISASSEMBLY
1035     dbg_SaveDisassembly( this );
1036 #endif
1037 
1038 #ifdef DBG_TRACE_BASIC
1039     lcl_PrepareTraceForModule( this );
1040 #endif
1041 
1042     return bRet;
1043 }
1044 
1045 /**************************************************************************
1046 *
1047 *   Syntax-Highlighting
1048 *
1049 **************************************************************************/
1050 
1051 void StarBASIC::Highlight( const String& rSrc, SbTextPortions& rList )
1052 {
1053     SbiTokenizer aTok( rSrc );
1054     aTok.Hilite( rList );
1055 }
1056 
1057