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