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