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