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 initialized once 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