xref: /trunk/main/basic/source/comp/sbcomp.cxx (revision cdf0e10c)
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