xref: /trunk/main/basic/source/comp/sbcomp.cxx (revision 0848378b)
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 
121 static void lcl_skipWhites( char*& rpc )
122 {
123 	while( *rpc == ' ' || *rpc == '\t' )
124 		++rpc;
125 }
126 
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 
138 inline bool lcl_isAlpha( char c )
139 {
140 	bool bRet = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
141 	return bRet;
142 }
143 
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 
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 
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 
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 
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 
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 
376 	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 
390 bool compareFunctionNetTime( FunctionItem* p1, FunctionItem* p2 )
391 {
392 	return (p1->m_dNetTime > p2->m_dNetTime);
393 }
394 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
1047 void StarBASIC::Highlight( const String& rSrc, SbTextPortions& rList )
1048 {
1049 	SbiTokenizer aTok( rSrc );
1050 	aTok.Hilite( rList );
1051 }
1052 
1053