3285 lines
94 KiB
Plaintext
3285 lines
94 KiB
Plaintext
/*
|
|
* $Id$
|
|
*/
|
|
|
|
/*
|
|
* Harbour Project source code:
|
|
* The Debugger
|
|
*
|
|
* Copyright 1999 Antonio Linares <alinares@fivetechsoft.com>
|
|
* www - http://www.harbour-project.org
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version, with one exception:
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this software; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
|
|
*
|
|
* As a special exception, the Harbour Project gives permission for
|
|
* additional uses of the text contained in its release of Harbour.
|
|
*
|
|
* The exception is that, if you link the Harbour libraries with other
|
|
* files to produce an executable, this does not by itself cause the
|
|
* resulting executable to be covered by the GNU General Public License.
|
|
* Your use of that executable is in no way restricted on account of
|
|
* linking the Harbour library code into it.
|
|
*
|
|
* This exception does not however invalidate any other reasons why
|
|
* the executable file might be covered by the GNU General Public License.
|
|
*
|
|
* This exception applies only to the code released by the Harbour
|
|
* Project under the name Harbour. If you copy code from other
|
|
* Harbour Project or Free Software Foundation releases into a copy of
|
|
* Harbour, as the General Public License permits, the exception does
|
|
* not apply to the code that you add in this way. To avoid misleading
|
|
* anyone as to the status of such modified files, you must delete
|
|
* this exception notice from them.
|
|
*
|
|
* If you write modifications of your own for Harbour, it is your choice
|
|
* whether to permit this exception to apply to your modifications.
|
|
* If you do not wish that, delete this exception notice.
|
|
*
|
|
*/
|
|
|
|
/* NOTE: Don't use SAY/DevOut()/DevPos() for screen output, otherwise
|
|
the debugger output may interfere with the applications output
|
|
redirection, and is also slower. [vszakats] */
|
|
|
|
|
|
//#pragma -es2
|
|
|
|
#include "hbclass.ch"
|
|
#include "hbmemvar.ch"
|
|
#include "box.ch"
|
|
#include "inkey.ch"
|
|
#include "common.ch"
|
|
#include "setcurs.ch"
|
|
#include "hbdebug.ch" //for "nMode" of __dbgEntry
|
|
|
|
#define NTRIM(x) (ALLTRIM(STR(x)))
|
|
|
|
#define ALTD_DISABLE 0
|
|
#define ALTD_ENABLE 1
|
|
|
|
|
|
/* Information structure stored in DATA aCallStack */
|
|
#define CSTACK_FUNCTION 1 //function name
|
|
#define CSTACK_LOCALS 2 //an array with local variables
|
|
#define CSTACK_LINE 3 //start line
|
|
#define CSTACK_MODULE 4 //module name (.PRG file)
|
|
#define CSTACK_STATICS 5 //an array with static variables
|
|
#define CSTACK_LEVEL 6 //eval stack level of the function
|
|
|
|
/* Information structure stored in aCallStack[n][ CSTACK_LOCALS ]
|
|
{ cLocalName, nLocalIndex, "Local", ProcName( 1 ), nLevel } */
|
|
#define VAR_NAME 1
|
|
#define VAR_POS 2
|
|
#define VAR_TYPE 3
|
|
#define VAR_FUNCNAME 4
|
|
#define VAR_LEVEL 5 //eval stack level of the function
|
|
|
|
/* Information structure stored in ::aWatch (watchpoints) */
|
|
#define WP_TYPE 1 //wp = watchpoint, tr = tracepoint
|
|
#define WP_EXPR 2 //source of an expression
|
|
#define WP_BLOCK 3 //codeblock to retrieve a value
|
|
|
|
/* Information structure stored in ::aTrace (tracepoints) */
|
|
#define TR_IDX 1 //index into ::aWatch item storing expression
|
|
#define TR_VALUE 2 //the current value of the expression
|
|
|
|
static s_oDebugger
|
|
static s_lExit := .F.
|
|
|
|
memvar __DbgStatics
|
|
|
|
|
|
procedure __dbgEntry( nMode, uParam1, uParam2, uParam3 ) // debugger entry point
|
|
|
|
local cProcName
|
|
local nVarIndex, cVarName
|
|
local nAt, nSFrame
|
|
LOCAL aTrace, uValue, lSuccess, nLen
|
|
|
|
|
|
IF( __MVSCOPE( "__DBGSTATICS" ) != HB_MV_PUBLIC )
|
|
public __DbgStatics
|
|
__DbgStatics := {}
|
|
ENDIF
|
|
|
|
do case
|
|
case nMode == HB_DBG_SHOWLINE
|
|
IF( s_lExit )
|
|
RETURN
|
|
ENDIF
|
|
IF( s_oDebugger:lTracepoints )
|
|
nLen := LEN(s_oDebugger:aTrace)
|
|
FOR nAt:=1 TO nLen
|
|
aTrace := s_oDebugger:aTrace[ nAt ]
|
|
uValue := GetWatchValue( s_oDebugger:aWatch[ aTrace[TR_IDX] ], @lSuccess, hb_dbg_ProcLevel()-1 )
|
|
IF( !lSuccess )
|
|
uValue := NIL
|
|
ENDIF
|
|
IF( (VALTYPE(uValue) != VALTYPE(aTrace[TR_VALUE])) .OR. ;
|
|
(uValue != aTrace[TR_VALUE]) )
|
|
aTrace[TR_VALUE] := uValue
|
|
s_oDebugger:lTrace :=.F.
|
|
s_oDebugger:lCodeblock :=.F.
|
|
s_oDebugger:lGo :=.F.
|
|
s_oDebugger:lToCursor :=.F.
|
|
s_oDebugger:lNextRoutine :=.F.
|
|
s_oDebugger:aCallStack[ 1 ][CSTACK_LINE] := uParam1
|
|
s_oDebugger:nProcLevel := hb_dbg_Proclevel()-IIF(PROCNAME(1)=="ALTD",2,1)
|
|
s_oDebugger:Activate()
|
|
RETURN
|
|
ENDIF
|
|
NEXT
|
|
ENDIF
|
|
|
|
// set the current line number on the CallStack
|
|
if s_oDebugger:lTrace
|
|
//In TRACE mode (step over procedure)
|
|
IF( s_oDebugger:nTraceLevel < Len( s_oDebugger:aCallStack ) )
|
|
s_oDebugger:lTrace := (! s_oDebugger:IsBreakPoint( uParam1, s_oDebugger:aCallStack[1][ CSTACK_MODULE ] )) .AND.;
|
|
(! HB_DBG_INVOKEDEBUG())
|
|
if s_oDebugger:lTrace
|
|
RETURN
|
|
ENDIF
|
|
ELSE
|
|
//Return back into a current procedure
|
|
s_oDebugger:lTrace := .f.
|
|
ENDIF
|
|
endif
|
|
|
|
IF( s_oDebugger:lToCursor )
|
|
IF( s_oDebugger:aToCursor[1] == uParam1 .AND. ;
|
|
s_oDebugger:aToCursor[2] == s_oDebugger:aCallStack[1][ CSTACK_MODULE ] )
|
|
s_oDebugger:lToCursor := .F.
|
|
ELSE
|
|
s_oDebugger:lToCursor := (! s_oDebugger:IsBreakPoint( uParam1, s_oDebugger:aCallStack[1][ CSTACK_MODULE ] )) .AND.;
|
|
(! HB_DBG_INVOKEDEBUG())
|
|
if s_oDebugger:lToCursor
|
|
RETURN
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
|
|
IF( s_oDebugger:lNextRoutine .AND. !HB_DBG_INVOKEDEBUG() )
|
|
s_oDebugger:lNextRoutine := (! s_oDebugger:IsBreakPoint( uParam1, s_oDebugger:aCallStack[1][ CSTACK_MODULE ] )) .AND.;
|
|
(! HB_DBG_INVOKEDEBUG())
|
|
if s_oDebugger:lNextRoutine
|
|
RETURN
|
|
ENDIF
|
|
ENDIF
|
|
|
|
if s_oDebugger:lGo
|
|
s_oDebugger:lGo := ! s_oDebugger:IsBreakPoint( uParam1, s_oDebugger:aCallStack[1][ CSTACK_MODULE ] )
|
|
endif
|
|
|
|
IF( s_oDebugger:lCodeblock )
|
|
s_oDebugger:lCodeblock := .F.
|
|
IF( !s_oDebugger:lCBTrace )
|
|
RETURN
|
|
ENDIF
|
|
ENDIF
|
|
|
|
s_oDebugger:aCallStack[ 1 ][CSTACK_LINE] := uParam1
|
|
if !s_oDebugger:lGo .or. HB_DBG_INVOKEDEBUG()
|
|
s_oDebugger:lGo := .F.
|
|
s_oDebugger:nProcLevel := hb_dbg_Proclevel()-IIF(PROCNAME(1)=="ALTD",2,1)
|
|
s_oDebugger:Activate()
|
|
endif
|
|
|
|
case nMode == HB_DBG_MODULENAME // called from hvm.c hb_vmModuleName()
|
|
// add a call to the stack but don't try to show the code yet
|
|
cProcName := ProcName( 1 )
|
|
|
|
if cProcName == "(_INITSTATICS)"
|
|
//module wide static variable
|
|
AADD( __dbgStatics, { strip_path( uParam1 ), {} } )
|
|
return // We can not use s_oDebugger yet, so we return
|
|
endif
|
|
|
|
IF( s_lExit )
|
|
RETURN
|
|
ENDIF
|
|
|
|
IF( s_oDebugger == NIL )
|
|
s_oDebugger := TDebugger():New()
|
|
ENDIF
|
|
if cProcName == "__EVAL" .OR. cProcName == "EVAL"
|
|
s_oDebugger:lCodeblock := .T.
|
|
ELSE
|
|
IF( s_oDebugger:lNextRoutine )
|
|
s_oDebugger:lNextRoutine :=.F.
|
|
ENDIF
|
|
endif
|
|
s_oDebugger:StackProc( uParam1, hb_dbg_ProcLevel()-1 )
|
|
|
|
case nMode == HB_DBG_ENDPROC
|
|
if ProcName( 1 ) == "(_INITSTATICS)"
|
|
return
|
|
endif
|
|
IF( s_lExit )
|
|
RETURN
|
|
ENDIF
|
|
if s_oDebugger:lCodeblock
|
|
s_oDebugger:lCodeblock := .F.
|
|
endif
|
|
s_oDebugger:EndProc()
|
|
|
|
case nMode == HB_DBG_LOCALNAME
|
|
IF( s_lExit )
|
|
RETURN
|
|
ENDIF
|
|
cProcName := IIF(s_oDebugger:lCodeblock, s_oDebugger:aCallStack[1][CSTACK_FUNCTION], ProcName( 1 ))
|
|
nVarIndex := uParam1
|
|
cVarName := IIF(valtype(uParam2)=='C',uParam2,'NIL')
|
|
nAt := hb_dbg_ProcLevel()-1
|
|
AAdd( s_oDebugger:aCallStack[ 1 ][ CSTACK_LOCALS ], ;
|
|
{ cVarName, nVarIndex, "Local", cProcName, nAt } )
|
|
if s_oDebugger:lShowLocals
|
|
if ( nAt := AScan( s_oDebugger:aVars,; // Is there another var with this name ?
|
|
{ | aVar | aVar[ 1 ] == cVarName } ) ) != 0
|
|
s_oDebugger:aVars[ nAt ] := ATAIL( s_oDebugger:aCallStack[ 1 ][ CSTACK_LOCALS ] )
|
|
else
|
|
AAdd( s_oDebugger:aVars, ATAIL( s_oDebugger:aCallStack[ 1 ][ CSTACK_LOCALS ] ) )
|
|
endif
|
|
endif
|
|
|
|
case nMode == HB_DBG_STATICNAME
|
|
nSFrame := uParam1
|
|
nVarIndex := uParam2
|
|
cVarName := uParam3
|
|
cProcName := ProcName( 1 )
|
|
if cProcName == "(_INITSTATICS)"
|
|
//module wide static variable
|
|
AAdd( ATAIL(__DbgStatics)[2], { cVarName, nVarIndex, "Static",, nSFrame } )
|
|
return // We can not use s_oDebugger yet, so we return
|
|
endif
|
|
|
|
IF( s_lExit )
|
|
RETURN
|
|
ENDIF
|
|
|
|
AAdd( s_oDebugger:aCallStack[ 1 ][ CSTACK_STATICS ], { cVarName, nVarIndex, "Static",, nSFrame } )
|
|
|
|
if s_oDebugger:lShowStatics
|
|
if ( nAt := AScan( s_oDebugger:aVars,; // Is there another var with this name ?
|
|
{ | aVar | aVar[ VAR_NAME ] == cVarName } ) ) != 0
|
|
s_oDebugger:aVars[ nAt ] := ATAIL( s_oDebugger:aCallStack[ 1 ][ CSTACK_STATICS ] )
|
|
else
|
|
AAdd( s_oDebugger:aVars, ATAIL( s_oDebugger:aCallStack[ 1 ][ CSTACK_STATICS ] ) )
|
|
endif
|
|
endif
|
|
|
|
endcase
|
|
|
|
return
|
|
|
|
CLASS TDebugger
|
|
|
|
DATA aWindows, nCurrentWindow
|
|
DATA oPullDown
|
|
DATA oWndCode, oWndCommand, oWndStack, oWndVars
|
|
DATA oBar, oBrwText, cPrgName, oBrwStack, oBrwVars, aVars
|
|
DATA cImage
|
|
DATA cAppImage, nAppRow, nAppCol, cAppColors, nAppCursor
|
|
DATA aBreakPoints
|
|
DATA aCallStack //stack of procedures with debug info
|
|
DATA aProcStack //stack of all procedures
|
|
DATA nProcLevel //procedure level where the debugger is currently
|
|
DATA aColors
|
|
DATA aWatch, aTrace, lTracepoints
|
|
DATA aLastCommands, nCommand, oGetListCommand
|
|
DATA lAnimate, lEnd, lCaseSensitive, lMonoDisplay, lSortVars
|
|
DATA cSearchString, cPathForFiles, cSettingsFileName, aPathDirs
|
|
DATA nTabWidth, nSpeed
|
|
DATA lShowPublics, lShowPrivates, lShowStatics, lShowLocals, lAll
|
|
DATA lShowCallStack
|
|
DATA lGo //stores if GO was requested
|
|
DATA lTrace //stores if TRACE over procedure was requested
|
|
DATA nTraceLevel //procedure level where TRACE was requested
|
|
DATA lCodeblock INIT .F.
|
|
DATA lActive INIT .F.
|
|
DATA lCBTrace INIT .T. //stores if codeblock tracing is allowed
|
|
DATA lToCursor INIT .F.
|
|
DATA aToCursor
|
|
DATA lNextRoutine INIT .F.
|
|
DATA oBrwPnt, oWndPnt
|
|
DATA lppo INIT .F. //view preprocessed output
|
|
DATA lRunAtStartup
|
|
DATA lLineNumbers INIT .T.
|
|
|
|
METHOD New()
|
|
METHOD Activate()
|
|
|
|
METHOD All()
|
|
|
|
METHOD Animate() INLINE If( ::lAnimate, ::Step(), nil )
|
|
|
|
METHOD BarDisplay()
|
|
METHOD BuildCommandWindow()
|
|
METHOD BuildBrowseStack()
|
|
|
|
METHOD CallStackProcessKey( nKey )
|
|
METHOD ClrModal() INLINE iif( ::lMonoDisplay, "N/W, W+/W, W/N, W+/N",;
|
|
"N/W, R/W, N/BG, R/BG" )
|
|
|
|
METHOD CodeWindowProcessKey( nKey )
|
|
METHOD Colors()
|
|
METHOD CommandWindowProcessKey( nKey )
|
|
METHOD EditColor( nColor, oBrwColors )
|
|
METHOD EditSet( nSet, oBrwSets )
|
|
METHOD EditVar( nVar )
|
|
METHOD EndProc()
|
|
METHOD Exit() INLINE ::lEnd := .t.
|
|
METHOD Go() INLINE ::RestoreAppStatus(), ::lGo := .t., ::Exit()
|
|
METHOD GoToLine( nLine )
|
|
METHOD HandleEvent()
|
|
METHOD Hide()
|
|
METHOD HideCallStack()
|
|
METHOD HideVars()
|
|
METHOD InputBox( cMsg, uValue, bValid, lEditable )
|
|
METHOD Inspect( uValue, cValueName )
|
|
METHOD IsBreakPoint( nLine, cPrgName )
|
|
METHOD LoadColors()
|
|
METHOD LoadSettings()
|
|
METHOD LoadVars()
|
|
METHOD LoadCallStack()
|
|
|
|
METHOD Local()
|
|
|
|
METHOD MonoDisplay()
|
|
METHOD NextWindow()
|
|
METHOD Open()
|
|
METHOD OpenPPO()
|
|
// METHOD Resume() INLINE IIF( LEN(::aCallStack[1])>0, ::ShowCodeLine( ::aCallStack[1][ CSTACK_LINE ], ::aCallStack[1][ CSTACK_MODULE ] ), NIL)
|
|
METHOD Resume() INLINE ::ShowCodeLine( 1 )
|
|
METHOD OSShell()
|
|
METHOD PathForFiles( cPathForFiles )
|
|
|
|
METHOD PrevWindow()
|
|
METHOD Private()
|
|
METHOD Public()
|
|
METHOD Quit() INLINE ::Exit(), ::Hide(), s_lExit := .T., s_oDebugger := NIL, __QUIT()
|
|
METHOD RefreshVars()
|
|
METHOD RestoreAppStatus()
|
|
METHOD RestoreSettings()
|
|
METHOD RunAtStartup() INLINE ::lRunAtStartup := ::oPullDown:GetItemByIdent( "ALTD" ):checked := !::lRunAtStartup
|
|
METHOD SaveAppStatus()
|
|
METHOD SaveSettings()
|
|
METHOD Show()
|
|
METHOD ShowAppScreen()
|
|
METHOD ShowCallStack()
|
|
//METHOD ShowCodeLine( nLine, cPrgName )
|
|
METHOD ShowCodeLine( nProc )
|
|
METHOD StackProc( cModuleName, nProcLevel )
|
|
METHOD ShowHelp( nTopic )
|
|
METHOD ShowVars()
|
|
METHOD RedisplayBreakpoints()
|
|
METHOD LocatePrgPath( cPrgName )
|
|
METHOD Sort() INLINE ASort( ::aVars,,, {|x,y| x[1] < y[1] } ),;
|
|
::lSortVars := .t.,;
|
|
iif( ::oBrwVars != nil, ::oBrwVars:RefreshAll(), nil ),;
|
|
iif( ::oWndVars != nil .and. ::oWndVars:lVisible, iif(!::lGo,::oBrwVars:ForceStable(),),)
|
|
|
|
METHOD Speed() INLINE ;
|
|
::nSpeed := ::InputBox( "Step delay (in tenths of a second)",;
|
|
::nSpeed )
|
|
|
|
METHOD Stack()
|
|
METHOD Static()
|
|
|
|
METHOD Step()
|
|
|
|
METHOD TabWidth() INLINE ;
|
|
::nTabWidth := ::InputBox( "Tab width", ::nTabWidth )
|
|
|
|
METHOD ToggleBreakPoint()
|
|
|
|
METHOD Trace() INLINE ::lTrace := .t., ::nTraceLevel := Len( ::aCallStack ),;
|
|
::Step() //forces a Step()
|
|
|
|
METHOD ToCursor()
|
|
METHOD NextRoutine()
|
|
METHOD CodeblockTrace() INLINE ::oPullDown:GetItemByIdent( "CODEBLOCK" ):checked := ::lCBTrace := ! ::lCBTrace
|
|
METHOD ViewSets()
|
|
METHOD WndVarsLButtonDown( nMRow, nMCol )
|
|
METHOD LineNumbers( lLineNumbers ) // Toggles numbering of source code lines
|
|
METHOD Locate()
|
|
METHOD FindNext()
|
|
METHOD FindPrevious()
|
|
METHOD RemoveWindow()
|
|
METHOD SearchLine()
|
|
METHOD ToggleAnimate() INLINE ::oPullDown:GetItemByIdent( "ANIMATE" ):checked := ::lAnimate := ! ::lAnimate
|
|
METHOD ToggleCaseSensitive() INLINE ::oPullDown:GetItemByIdent( "CASE" ):checked := ::lCaseSensitive := ! ::lCaseSensitive
|
|
METHOD ShowWorkAreas() INLINE __dbgShowWorkAreas( Self )
|
|
|
|
METHOD TracepointAdd( cExpr )
|
|
METHOD WatchpointAdd( cExpr )
|
|
METHOD WatchpointDel( nPos )
|
|
METHOD WatchpointsShow()
|
|
METHOD WatchpointsHide()
|
|
METHOD WatchpointEdit( nVar )
|
|
METHOD WatchGetInfo( aVar )
|
|
|
|
METHOD VarGetInfo( aVar )
|
|
METHOD VarGetValue( aVar )
|
|
METHOD VarSetValue( aVar, uValue )
|
|
|
|
METHOD ResizeWindows( oWindow )
|
|
METHOD NotSupported() INLINE Alert( "Not implemented yet!" )
|
|
|
|
ENDCLASS
|
|
|
|
|
|
METHOD New() CLASS TDebugger
|
|
|
|
s_oDebugger := Self
|
|
|
|
::aColors := {"W+/BG","N/BG","R/BG","N+/BG","W+/B","GR+/B","W/B","N/W","R/W","N/BG","R/BG"}
|
|
::lMonoDisplay := .f.
|
|
::aWindows := {}
|
|
::nCurrentWindow := 1
|
|
::lAnimate := .f.
|
|
::lEnd := .f.
|
|
::lTrace := .f.
|
|
::aBreakPoints := {}
|
|
::aWatch := {}
|
|
::aTrace := {}
|
|
::lTracepoints := .F.
|
|
::aCallStack := {}
|
|
::aProcStack := {}
|
|
::aVars := {}
|
|
::lCaseSensitive := .f.
|
|
::cSearchString := ""
|
|
|
|
// default the search path for files to the current directory
|
|
// that way if the source is in the same directory it will still be found even if the application
|
|
// changes the current directory with the SET DEFAULT command
|
|
::cPathForFiles := getenv( "HB_DBG_PATH" )
|
|
if empty( ::cPathForFiles )
|
|
::cPathForFiles := getenv( "PATH" )
|
|
endif
|
|
::nTabWidth := 4
|
|
::nSpeed := 0
|
|
::lShowCallStack := .f.
|
|
::lShowPublics := .f.
|
|
::lShowPrivates := .f.
|
|
::lShowStatics := .f.
|
|
::lShowLocals := .f.
|
|
::lAll := .f.
|
|
::lSortVars := .f.
|
|
::cSettingsFileName := "init.cld"
|
|
::lRunAtStartup := .t. //Clipper compatible
|
|
|
|
if File( ::cSettingsFileName )
|
|
::LoadSettings()
|
|
endif
|
|
::lGo := ::lRunAtStartup
|
|
|
|
::oPullDown := __dbgBuildMenu( Self )
|
|
::oPulldown:GetItemByIdent( "ALTD" ):Checked := ::lRunAtStartup
|
|
|
|
::oWndCode := TDbWindow():New( 1, 0, MaxRow() - 6, MaxCol() )
|
|
::oWndCode:Cargo := { ::oWndCode:nTop, ::oWndCode:nLeft }
|
|
::oWndCode:bKeyPressed := { | nKey | ::CodeWindowProcessKey( nKey ) }
|
|
::oWndCode:bGotFocus := { || ::oGetListCommand:SetFocus(), SetCursor( SC_SPECIAL1 ), ;
|
|
SetPos( ::oWndCode:Cargo[1],::oWndCode:Cargo[2] ) }
|
|
::oWndCode:bLostFocus := { || ::oWndCode:Cargo[1] := Row(), ::oWndCode:Cargo[2] := Col(), ;
|
|
SetCursor( SC_NONE ) }
|
|
|
|
AAdd( ::aWindows, ::oWndCode )
|
|
|
|
::BuildCommandWindow()
|
|
::BuildBrowseStack()
|
|
|
|
return Self
|
|
|
|
|
|
METHOD Activate() CLASS TDebugger
|
|
|
|
::LoadCallStack()
|
|
IF( ! ::lActive )
|
|
::lActive := .T.
|
|
::Show()
|
|
if ::lShowCallStack
|
|
::ShowCallStack()
|
|
endif
|
|
ELSE
|
|
::SaveAppStatus()
|
|
ENDIF
|
|
::loadVars()
|
|
::ShowVars()
|
|
IF( ::oWndPnt != NIL )
|
|
::WatchpointsShow()
|
|
ENDIF
|
|
// show the topmost procedure
|
|
::ShowCodeLine( 1 ) //::aCallStack[1][ CSTACK_LINE ], ::aCallStack[1][ CSTACK_MODULE ] )
|
|
::HandleEvent()
|
|
|
|
return nil
|
|
|
|
METHOD All() CLASS TDebugger
|
|
|
|
::lShowPublics := ::lShowPrivates := ::lShowStatics := ;
|
|
::lShowLocals := ::lAll := ! ::lAll
|
|
|
|
::RefreshVars()
|
|
|
|
return nil
|
|
|
|
METHOD BarDisplay() CLASS TDebugger
|
|
|
|
local cClrItem := __DbgColors()[ 8 ]
|
|
local cClrHotKey := __DbgColors()[ 9 ]
|
|
|
|
DispBegin()
|
|
SetColor( cClrItem )
|
|
@ MaxRow(), 0 CLEAR TO MaxRow(), MaxCol()
|
|
|
|
DispOutAt( MaxRow(), 0,;
|
|
"F1-Help F2-Zoom F3-Repeat F4-User F5-Go F6-WA F7-Here F8-Step F9-BkPt F10-Trace",;
|
|
cClrItem )
|
|
DispOutAt( MaxRow(), 0, "F1", cClrHotKey )
|
|
DispOutAt( MaxRow(), 8, "F2", cClrHotKey )
|
|
DispOutAt( MaxRow(), 16, "F3", cClrHotKey )
|
|
DispOutAt( MaxRow(), 26, "F4", cClrHotKey )
|
|
DispOutAt( MaxRow(), 34, "F5", cClrHotKey )
|
|
DispOutAt( MaxRow(), 40, "F6", cClrHotKey )
|
|
DispOutAt( MaxRow(), 46, "F7", cClrHotKey )
|
|
DispOutAt( MaxRow(), 54, "F8", cClrHotKey )
|
|
DispOutAt( MaxRow(), 62, "F9", cClrHotKey )
|
|
DispOutAt( MaxRow(), 70, "F10", cClrHotKey )
|
|
DispEnd()
|
|
|
|
return nil
|
|
|
|
METHOD BuildCommandWindow() CLASS TDebugger
|
|
|
|
local GetList := {}, oGet
|
|
local cCommand
|
|
|
|
::oWndCommand := TDbWindow():New( MaxRow() - 5, 0, MaxRow() - 1, MaxCol(),;
|
|
"Command" )
|
|
|
|
::oWndCommand:bGotFocus := { || ::oGetListCommand:SetFocus(), SetCursor( SC_NORMAL ) }
|
|
::oWndCommand:bLostFocus := { || SetCursor( SC_NONE ) }
|
|
::oWndCommand:bKeyPressed := { | nKey | ::CommandWindowProcessKey( nKey ) }
|
|
::oWndCommand:bPainted := { || DispOutAt( ::oWndCommand:nBottom - 1,;
|
|
::oWndCommand:nLeft + 1, "> ", __DbgColors()[ 2 ] ),;
|
|
oGet:ColorDisp( Replicate( __DbgColors()[ 2 ] + ",", 5 ) ),;
|
|
hb_ClrArea( ::oWndCommand:nTop + 1, ::oWndCommand:nLeft + 1,;
|
|
::oWndCommand:nBottom - 2, ::oWndCommand:nRight - 1,;
|
|
iif( ::lMonoDisplay, 15, HB_ColorToN( __DbgColors()[ 2 ] ) ) ) }
|
|
AAdd( ::aWindows, ::oWndCommand )
|
|
|
|
::aLastCommands := {}
|
|
::nCommand := 0
|
|
|
|
cCommand := Space( ::oWndCommand:nRight - ::oWndCommand:nLeft - 3 )
|
|
// We don't use the GET command here to avoid the painting of the GET
|
|
AAdd( GetList, oGet := Get():New( ::oWndCommand:nBottom - 1, ::oWndCommand:nLeft + 3,;
|
|
{ | u | iif( PCount() > 0, cCommand := u, cCommand ) }, "cCommand" ) )
|
|
oGet:ColorSpec := Replicate( __DbgColors()[ 2 ] + ",", 5 )
|
|
::oGetListCommand := HBGetList():New( GetList )
|
|
|
|
return nil
|
|
|
|
METHOD BuildBrowseStack() CLASS TDebugger
|
|
|
|
if ::oBrwStack == nil
|
|
::oBrwStack := TBrowseNew( 2, MaxCol() - 14, MaxRow() - 7, MaxCol() - 1 )
|
|
::oBrwStack:ColorSpec := ::aColors[ 3 ] + "," + ::aColors[ 4 ] + "," + ::aColors[ 5 ]
|
|
::oBrwStack:GoTopBlock := { || ::oBrwStack:Cargo := 1 }
|
|
::oBrwStack:GoBottomBlock := { || ::oBrwStack:Cargo := Len( ::aProcStack ) }
|
|
::oBrwStack:SkipBlock = { | nSkip, nOld | nOld := ::oBrwStack:Cargo,;
|
|
::oBrwStack:Cargo += nSkip,;
|
|
::oBrwStack:Cargo := Min( Max( ::oBrwStack:Cargo, 1 ),;
|
|
Len( ::aProcStack ) ), ::oBrwStack:Cargo - nOld }
|
|
|
|
::oBrwStack:Cargo := 1 // Actual highligthed row
|
|
|
|
::oBrwStack:AddColumn( TBColumnNew( "", { || If( Len( ::aProcStack ) > 0,;
|
|
PadC( ::aProcStack[ ::oBrwStack:Cargo ][1], 14 ), Space( 14 ) ) } ) )
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD CallStackProcessKey( nKey ) CLASS TDebugger
|
|
|
|
local n, nSkip, lUpdate := .f.
|
|
|
|
do case
|
|
case nKey == K_HOME
|
|
if ::oBrwStack:Cargo > 1
|
|
::oBrwStack:GoTop()
|
|
::oBrwStack:ForceStable()
|
|
lUpdate = .t.
|
|
endif
|
|
|
|
case nKey == K_END
|
|
if ::oBrwStack:Cargo < Len( ::aProcStack )
|
|
::oBrwStack:GoBottom()
|
|
::oBrwStack:ForceStable()
|
|
lUpdate = .t.
|
|
endif
|
|
|
|
case nKey == K_UP
|
|
if ::oBrwStack:Cargo > 1
|
|
::oBrwStack:Up()
|
|
::oBrwStack:ForceStable()
|
|
lUpdate = .t.
|
|
endif
|
|
|
|
case nKey == K_DOWN
|
|
if ::oBrwStack:Cargo < Len( ::aProcStack )
|
|
::oBrwStack:Down()
|
|
::oBrwStack:ForceStable()
|
|
lUpdate = .t.
|
|
endif
|
|
|
|
case nKey == K_PGUP
|
|
::oBrwStack:PageUp()
|
|
::oBrwStack:ForceStable()
|
|
lUpdate = .t.
|
|
|
|
case nKey == K_PGDN
|
|
::oBrwStack:PageDown()
|
|
::oBrwStack:ForceStable()
|
|
lUpdate = .t.
|
|
|
|
case nKey == K_LBUTTONDOWN
|
|
if ( nSkip := MRow() - ::oWndStack:nTop - ::oBrwStack:RowPos ) != 0
|
|
if nSkip > 0
|
|
for n = 1 to nSkip
|
|
::oBrwStack:Down()
|
|
::oBrwStack:Stabilize()
|
|
next
|
|
else
|
|
for n = 1 to nSkip + 2 step -1
|
|
::oBrwStack:Up()
|
|
::oBrwStack:Stabilize()
|
|
next
|
|
endif
|
|
::oBrwStack:ForceStable()
|
|
endif
|
|
lUpdate = .t.
|
|
endcase
|
|
|
|
if lUpdate
|
|
if ::oWndVars != nil .AND. ::oWndVars:lVisible
|
|
::LoadVars()
|
|
::ShowVars()
|
|
endif
|
|
|
|
// jump to source line for a function
|
|
/*
|
|
if ::aCallStack[ ::oBrwStack:Cargo ][ CSTACK_LINE ] != nil
|
|
::ShowCodeLine( ::aCallStack[ ::oBrwStack:Cargo ][ CSTACK_LINE ], ::aCallStack[ ::oBrwStack:Cargo ][ CSTACK_MODULE ] )
|
|
else
|
|
::GotoLine( 1 )
|
|
endif
|
|
*/
|
|
::ShowCodeLine( ::oBrwStack:Cargo )
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD CodeWindowProcessKey( nKey ) CLASS TDebugger
|
|
|
|
do case
|
|
case nKey == K_HOME
|
|
::oBrwText:GoTop()
|
|
if ::oWndCode:lFocused
|
|
SetCursor( SC_SPECIAL1 )
|
|
endif
|
|
|
|
case nKey == K_END
|
|
::oBrwText:GoBottom()
|
|
::oBrwText:nCol = ::oWndCode:nLeft + 1
|
|
::oBrwText:nFirstCol = ::oWndCode:nLeft + 1
|
|
SetPos( Row(), ::oWndCode:nLeft + 1 )
|
|
if ::oWndCode:lFocused
|
|
SetCursor( SC_SPECIAL1 )
|
|
endif
|
|
|
|
case nKey == K_LEFT
|
|
::oBrwText:Left()
|
|
|
|
case nKey == K_RIGHT
|
|
::oBrwText:Right()
|
|
|
|
case nKey == K_UP
|
|
::oBrwText:Up()
|
|
|
|
case nKey == K_DOWN
|
|
::oBrwText:Down()
|
|
|
|
case nKey == K_PGUP
|
|
::oBrwText:PageUp()
|
|
|
|
case nKey == K_PGDN
|
|
::oBrwText:PageDown()
|
|
|
|
endcase
|
|
|
|
return nil
|
|
|
|
METHOD Colors() CLASS TDebugger
|
|
|
|
local oWndColors := TDbWindow():New( 4, 5, 16, MaxCol() - 5,;
|
|
"Debugger Colors[1..11]", ::ClrModal() )
|
|
local aColors := { "Border", "Text", "Text High", "Text PPO", "Text Selected",;
|
|
"Text High Sel.", "Text PPO Sel.", "Menu", "Menu High",;
|
|
"Menu Selected", "Menu High Sel." }
|
|
|
|
local oBrwColors := TBrowseNew( oWndColors:nTop + 1, oWndColors:nLeft + 1,;
|
|
oWndColors:nBottom - 1, oWndColors:nRight - 1 )
|
|
local nWidth := oWndColors:nRight - oWndColors:nLeft - 1
|
|
local oCol
|
|
|
|
if ::lMonoDisplay
|
|
Alert( "Monochrome display" )
|
|
return nil
|
|
endif
|
|
oBrwColors:Cargo :={ 1,{}} // Actual highligthed row
|
|
oBrwColors:ColorSpec := ::ClrModal()
|
|
oBrwColors:GOTOPBLOCK := { || oBrwColors:cargo[ 1 ]:= 1 }
|
|
oBrwColors:GoBottomBlock := { || oBrwColors:cargo[ 1 ]:= Len(oBrwColors:cargo[ 2 ][ 1 ])}
|
|
oBrwColors:SkipBlock := { |nPos| ( nPos:= ArrayBrowseSkip(nPos, oBrwColors), oBrwColors:cargo[ 1 ]:= ;
|
|
oBrwColors:cargo[ 1 ] + nPos,nPos ) }
|
|
|
|
oBrwColors:AddColumn( ocol := TBColumnNew( "", { || PadR( aColors[ oBrwColors:Cargo[1] ], 14 ) } ) )
|
|
oCol:DefColor:={1,2}
|
|
aadd(oBrwColors:Cargo[2],acolors)
|
|
oBrwColors:AddColumn( oCol := TBColumnNew( "",;
|
|
{ || PadR( '"' + ::aColors[ oBrwColors:Cargo[1] ] + '"', nWidth - 15 ) } ) )
|
|
aadd(oBrwColors:Cargo[2],acolors)
|
|
oCol:DefColor:={1,3}
|
|
ocol:width:=50
|
|
oBrwColors:autolite:=.f.
|
|
|
|
oWndColors:bPainted := { || oBrwColors:ForceStable(),RefreshVarsS(oBrwColors)}
|
|
|
|
oWndColors:bKeyPressed := { | nKey | SetsKeyPressed( nKey, oBrwColors,;
|
|
Len( aColors ), oWndColors, "Debugger Colors",;
|
|
{ || ::EditColor( oBrwColors:Cargo[1], oBrwColors ) } ) }
|
|
oWndColors:ShowModal()
|
|
|
|
::LoadColors()
|
|
|
|
RETURN NIL
|
|
|
|
|
|
METHOD CommandWindowProcessKey( nKey ) CLASS TDebugger
|
|
|
|
local cCommand, cResult, oE
|
|
local bLastHandler
|
|
local lDisplay
|
|
|
|
do case
|
|
case nKey == K_UP
|
|
if ::nCommand > 0
|
|
::oGetListCommand:oGet:VarPut( ::aLastCommands[ ::nCommand ] )
|
|
::oGetListCommand:oGet:Buffer := ::aLastCommands[ ::nCommand ]
|
|
::oGetListCommand:oGet:Pos := 1
|
|
::oGetListCommand:oGet:Display()
|
|
if ::nCommand > 1
|
|
::nCommand--
|
|
endif
|
|
endif
|
|
|
|
case nKey == K_DOWN
|
|
if ::nCommand > 0 .AND. ::nCommand <= Len( ::aLastCommands )
|
|
::oGetListCommand:oGet:VarPut( ::aLastCommands[ ::nCommand ] )
|
|
::oGetListCommand:oGet:Buffer := ::aLastCommands[ ::nCommand ]
|
|
::oGetListCommand:oGet:Pos := 1
|
|
::oGetListCommand:oGet:Display()
|
|
if ::nCommand < Len( ::aLastCommands )
|
|
::nCommand++
|
|
endif
|
|
endif
|
|
|
|
case nKey == K_ENTER
|
|
cCommand := ::oGetListCommand:oGet:VarGet()
|
|
|
|
if ! Empty( cCommand )
|
|
AAdd( ::aLastCommands, cCommand )
|
|
::nCommand++
|
|
::oWndCommand:ScrollUp( 1 )
|
|
endif
|
|
|
|
do case
|
|
case Empty( cCommand )
|
|
lDisplay = .f.
|
|
|
|
case SubStr( LTrim( cCommand ), 1, 2 ) == "??" .or. ;
|
|
SubStr( LTrim( cCommand ), 1, 1 ) == "?"
|
|
lDisplay := !Empty( cResult := DoCommand( Self,cCommand ) )
|
|
|
|
case Upper( SubStr( LTrim( cCommand ), 1, 4 ) ) == "ANIM" .or. ;
|
|
Upper( SubStr( LTrim( cCommand ), 1, 7 ) ) == "ANIMATE"
|
|
::lAnimate = .t.
|
|
::Animate()
|
|
SetCursor( SC_NORMAL )
|
|
lDisplay = .f.
|
|
|
|
case Upper( SubStr( LTrim( cCommand ), 1, 3 ) ) == "DOS"
|
|
::OsShell()
|
|
SetCursor( SC_NORMAL )
|
|
lDisplay = .f.
|
|
|
|
case Upper( SubStr( LTrim( cCommand ), 1, 4 ) ) == "HELP"
|
|
::ShowHelp()
|
|
lDisplay = .f.
|
|
|
|
case Upper( SubStr( LTrim( cCommand ), 1, 4 ) ) == "QUIT"
|
|
::Quit()
|
|
|
|
case Upper( SubStr( LTrim( cCommand ), 1, 6 ) ) == "OUTPUT"
|
|
SetCursor( SC_NONE )
|
|
::ShowAppScreen()
|
|
SetCursor( SC_NORMAL )
|
|
lDisplay = .f.
|
|
|
|
case Upper( SubStr( LTrim( cCommand ), 1, 3 ) ) == "WP "
|
|
//add watchpoint
|
|
::WatchpointAdd( SUBSTR( LTRIM(cCommand), 3 ) )
|
|
lDisplay = .f.
|
|
|
|
case Upper( SubStr( LTrim( cCommand ), 1, 3 ) ) == "TP "
|
|
//add tracepoint
|
|
::TracepointAdd( SUBSTR( LTRIM(cCommand), 3 ) )
|
|
lDisplay = .f.
|
|
|
|
otherwise
|
|
cResult = "Command error"
|
|
lDisplay = .t.
|
|
|
|
endcase
|
|
|
|
DispOutAt( ::oWndCommand:nBottom - 1, ::oWndCommand:nLeft + 1,;
|
|
Space( ::oWndCommand:nRight - ::oWndCommand:nLeft - 1 ),;
|
|
__DbgColors()[ 2 ] )
|
|
if lDisplay
|
|
DispOutAt( ::oWndCommand:nBottom - 1, ::oWndCommand:nLeft + 3,;
|
|
cResult, __DbgColors()[ 2 ] )
|
|
::oWndCommand:ScrollUp( 1 )
|
|
endif
|
|
DispOutAt( ::oWndCommand:nBottom - 1, ::oWndCommand:nLeft + 1, "> ",;
|
|
__DbgColors()[ 2 ] )
|
|
cCommand := Space( ::oWndCommand:nRight - ::oWndCommand:nLeft - 3 )
|
|
::oGetListCommand:oGet:VarPut( cCommand )
|
|
::oGetListCommand:oGet:Buffer := cCommand
|
|
::oGetListCommand:oGet:Pos := 1
|
|
::oGetListCommand:oGet:Display()
|
|
|
|
otherwise
|
|
::oGetListCommand:GetApplyKey( nKey )
|
|
endcase
|
|
|
|
return nil
|
|
|
|
METHOD EditColor( nColor, oBrwColors ) CLASS TDebugger
|
|
|
|
local GetList := {}
|
|
local lPrevScore := Set( _SET_SCOREBOARD, .f. )
|
|
local lPrevExit := Set( _SET_EXIT, .t. )
|
|
local cColor := PadR( '"' + ::aColors[ nColor ] + '"',;
|
|
oBrwColors:getColumn(2):Width )
|
|
|
|
oBrwColors:RefreshCurrent()
|
|
oBrwColors:ForceStable()
|
|
|
|
SetCursor( SC_NORMAL )
|
|
@ Row(), Col() + 15 GET cColor COLOR SubStr( ::ClrModal(), 5 ) ;
|
|
VALID iif( Type( cColor ) != "C", ( Alert( "Must be string" ), .f. ), .t. )
|
|
|
|
READ
|
|
SetCursor( SC_NONE )
|
|
|
|
Set( _SET_SCOREBOARD, lPrevScore )
|
|
Set( _SET_EXIT, lPrevExit )
|
|
|
|
if LastKey() != K_ESC
|
|
::aColors[ nColor ] := &cColor
|
|
endif
|
|
|
|
oBrwColors:RefreshCurrent()
|
|
oBrwColors:ForceStable()
|
|
|
|
return nil
|
|
|
|
METHOD EditSet( nSet, oBrwSets ) CLASS TDebugger
|
|
|
|
local GetList := {}
|
|
local lPrevScore := Set( _SET_SCOREBOARD, .f. )
|
|
local lPrevExit := Set( _SET_EXIT, .t. )
|
|
local cSet := PadR( ValToStr( Set( nSet ) ), oBrwSets:getColumn(2):Width )
|
|
local cType := VALTYPE(SET(nSet))
|
|
|
|
oBrwSets:RefreshCurrent()
|
|
oBrwSets:ForceStable()
|
|
|
|
SetCursor( SC_NORMAL )
|
|
@ Row(), Col()+13 GET cSet COLOR SubStr( ::ClrModal(), 5 ) ;
|
|
VALID iif( Type(cSet) != cType, (Alert( "Must be of type '"+cType+"'" ), .f. ), .t. )
|
|
|
|
|
|
READ
|
|
SetCursor( SC_NONE )
|
|
|
|
Set( _SET_SCOREBOARD, lPrevScore )
|
|
Set( _SET_EXIT, lPrevExit )
|
|
|
|
if LastKey() != K_ESC
|
|
Set( nSet, &cSet )
|
|
endif
|
|
|
|
oBrwSets:RefreshCurrent()
|
|
oBrwSets:ForceStable()
|
|
|
|
return nil
|
|
|
|
METHOD VarGetValue( aVar ) CLASS TDebugger
|
|
LOCAL nProcLevel, uValue
|
|
LOCAL cProc
|
|
LOCAL cType:=LEFT(aVar[ VAR_TYPE ], 1)
|
|
|
|
IF( cType == "L" )
|
|
nProcLevel := hb_dbg_procLevel() - aVar[ VAR_LEVEL ]
|
|
cProc := aVar[ VAR_FUNCNAME ]
|
|
uValue := hb_dbg_vmVarLGet( nProcLevel, aVar[ VAR_POS ] )
|
|
|
|
ELSEIF( cType == "S" )
|
|
uValue := hb_dbg_vmVarSGet( aVar[VAR_LEVEL], aVar[ VAR_POS ] )
|
|
|
|
ELSE
|
|
//Public or Private
|
|
uValue := aVar[ VAR_POS ]
|
|
ENDIF
|
|
|
|
RETURN uValue
|
|
|
|
|
|
METHOD VarSetValue( aVar, uValue ) CLASS TDebugger
|
|
LOCAL nProcLevel
|
|
LOCAL cProc
|
|
LOCAL cType:=LEFT(aVar[ VAR_TYPE ], 1)
|
|
|
|
IF( cType == "L" )
|
|
nProcLevel := hb_dbg_procLevel() - aVar[VAR_LEVEL] //skip debugger stack
|
|
cProc := aVar[ VAR_FUNCNAME ]
|
|
hb_dbg_vmVarLSet( nProcLevel, aVar[ VAR_POS ], uValue )
|
|
|
|
ELSEIF( cType == "S" )
|
|
hb_dbg_vmVarSSet( aVar[VAR_LEVEL], aVar[ VAR_POS ], uValue )
|
|
|
|
ELSE
|
|
//Public or Private
|
|
aVar[ VAR_POS ] := uValue
|
|
&( aVar[ VAR_NAME ] ) := uValue
|
|
|
|
ENDIF
|
|
|
|
RETURN self
|
|
|
|
|
|
METHOD EditVar( nVar ) CLASS TDebugger
|
|
|
|
local cVarName := ::aVars[ nVar ][ 1 ]
|
|
local uVarValue := ::aVars[ nVar ][ 2 ]
|
|
local cVarType := ::aVars[ nVar ][ 3 ]
|
|
local aArray
|
|
local cVarStr
|
|
|
|
uVarValue := ::VarGetValue( ::aVars[ nVar ] )
|
|
|
|
do case
|
|
case ValType( uVarValue ) == "A"
|
|
::InputBox( cVarName, uVarValue,, .f. )
|
|
|
|
case ValType( uVarValue ) == "O"
|
|
::InputBox( cVarName, uVarValue,, .f. )
|
|
|
|
otherwise
|
|
cVarStr := ::InputBox( cVarName, ValToStr( uVarValue ),;
|
|
{ | u | If( Type( u ) == "UE", ( Alert( "Expression error" ), .f. ), .t. ) } )
|
|
endcase
|
|
|
|
if LastKey() != K_ESC
|
|
do case
|
|
case cVarStr == "{ ... }"
|
|
//aArray := ::VarGetValue( ::aVars[ nVar ] )
|
|
if Len( uVarValue ) > 0
|
|
__DbgArrays( uVarValue, cVarName )
|
|
else
|
|
Alert( "Array is empty" )
|
|
endif
|
|
|
|
case Upper( SubStr( cVarStr, 1, 5 ) ) == "CLASS"
|
|
__DbgObject( uVarValue, cVarName )
|
|
|
|
otherwise
|
|
::VarSetValue( ::aVars[ nVar ], &cVarStr )
|
|
endcase
|
|
endif
|
|
|
|
::oBrwVars:RefreshCurrent()
|
|
::oBrwVars:ForceStable()
|
|
|
|
return nil
|
|
|
|
METHOD EndProc() CLASS TDebugger
|
|
|
|
if Len( ::aCallStack ) > 0
|
|
ADel( ::aCallStack, 1 )
|
|
ASize( ::aCallStack, Len( ::aCallStack ) - 1 )
|
|
if ::oBrwStack != nil .and. ! ::lTrace
|
|
::oBrwStack:RefreshAll()
|
|
endif
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD HandleEvent() CLASS TDebugger
|
|
|
|
local nPopup, oWnd
|
|
local nKey, nMRow, nMCol, n
|
|
local nLastKey
|
|
|
|
/* Save LastKey() */
|
|
nLastKey := LastKey()
|
|
|
|
if ::lAnimate
|
|
if ::nSpeed != 0
|
|
Inkey( ::nSpeed / 10 )
|
|
endif
|
|
if HB_DBG_INVOKEDEBUG() //NextKey() == K_ALT_D
|
|
::lAnimate := .f.
|
|
else
|
|
::Step()
|
|
RETURN nil
|
|
//KEYBOARD Chr( 255 ) // Forces a Step(). Only 0-255 range is supported
|
|
endif
|
|
endif
|
|
|
|
::lEnd := .f.
|
|
|
|
while ! ::lEnd
|
|
|
|
nKey := InKey( 0, INKEY_ALL )
|
|
|
|
do case
|
|
case nKey == K_ALT_X
|
|
s_oDebugger:Quit()
|
|
|
|
case ::oPullDown:IsOpen()
|
|
::oPullDown:ProcessKey( nKey )
|
|
if ::oPullDown:nOpenPopup == 0 // Closed
|
|
::aWindows[ ::nCurrentWindow ]:SetFocus( .t. )
|
|
endif
|
|
|
|
case nKey == K_LDBLCLK
|
|
if MRow() == 0
|
|
|
|
elseif MRow() == MaxRow()
|
|
|
|
else
|
|
nMRow := MRow()
|
|
nMCol := MCol()
|
|
for n := 1 to Len( ::aWindows )
|
|
if ::aWindows[ n ]:IsOver( nMRow, nMCol )
|
|
if ! ::aWindows[ n ]:lFocused
|
|
::aWindows[ ::nCurrentWindow ]:SetFocus( .f. )
|
|
::nCurrentWindow := n
|
|
::aWindows[ n ]:SetFocus( .t. )
|
|
endif
|
|
::aWindows[ n ]:LDblClick( nMRow, nMCol )
|
|
exit
|
|
endif
|
|
next
|
|
endif
|
|
|
|
case nKey == K_LBUTTONDOWN
|
|
if MRow() == 0
|
|
if ( nPopup := ::oPullDown:GetItemOrdByCoors( 0, MCol() ) ) != 0
|
|
if ! ::oPullDown:IsOpen()
|
|
if ::oWndCode:lFocused
|
|
Eval( ::oWndCode:bLostFocus )
|
|
endif
|
|
SetCursor( SC_NONE )
|
|
endif
|
|
::oPullDown:ShowPopup( nPopup )
|
|
endif
|
|
|
|
elseif MRow() == MaxRow()
|
|
|
|
else
|
|
nMRow := MRow()
|
|
nMCol := MCol()
|
|
for n := 1 to Len( ::aWindows )
|
|
if ::aWindows[ n ]:IsOver( nMRow, nMCol )
|
|
if ! ::aWindows[ n ]:lFocused
|
|
::aWindows[ ::nCurrentWindow ]:SetFocus( .f. )
|
|
::nCurrentWindow := n
|
|
::aWindows[ n ]:SetFocus( .t. )
|
|
endif
|
|
::aWindows[ n ]:LButtonDown( nMRow, nMCol )
|
|
exit
|
|
endif
|
|
next
|
|
endif
|
|
|
|
case nKey == K_RBUTTONDOWN
|
|
|
|
/*case nKey == K_ESC
|
|
::RestoreAppStatus()
|
|
s_oDebugger := nil
|
|
s_lExit := .T.
|
|
DispEnd()
|
|
::Exit()*/
|
|
|
|
case nKey == K_UP .or. nKey == K_DOWN .or. nKey == K_HOME .or. ;
|
|
nKey == K_END .or. nKey == K_ENTER .or. nKey == K_PGDN .or. ;
|
|
nKey == K_PGUP .or. nKey == K_DEL .or. nKey == K_LEFT .or. ;
|
|
nKey == K_RIGHT
|
|
oWnd := ::aWindows[ ::nCurrentWindow ]
|
|
oWnd:KeyPressed( nKey )
|
|
|
|
case nKey == K_F1
|
|
::ShowHelp()
|
|
|
|
case nKey == K_F4
|
|
::ShowAppScreen()
|
|
|
|
case nKey == K_F5
|
|
// we are starting to run again so reset to the deepest call if
|
|
// displaying stack
|
|
if ! ::oBrwStack == nil
|
|
::oBrwStack:GoTop()
|
|
endif
|
|
::Go()
|
|
|
|
case nKey == K_CTRL_F5
|
|
::NextRoutine()
|
|
|
|
case nKey == K_F6
|
|
::ShowWorkAreas()
|
|
|
|
case nKey == K_F7
|
|
::ToCursor()
|
|
|
|
case nKey == K_F8 .or. nKey == 255
|
|
::Step()
|
|
|
|
case nKey == K_F9
|
|
::ToggleBreakPoint()
|
|
|
|
case nKey == K_F10
|
|
::Trace()
|
|
|
|
case nKey == K_TAB
|
|
::NextWindow()
|
|
|
|
case nKey == K_SH_TAB
|
|
::PrevWindow()
|
|
|
|
case ::oWndCommand:lFocused .and. nKey < 272 // Alt
|
|
::oWndCommand:KeyPressed( nKey )
|
|
|
|
otherwise
|
|
if ( nPopup := ::oPullDown:GetHotKeyPos( __dbgAltToKey( nKey ) ) ) != 0
|
|
if ::oPullDown:nOpenPopup != nPopup
|
|
SetCursor( SC_NONE )
|
|
::oPullDown:ShowPopup( nPopup )
|
|
endif
|
|
endif
|
|
endcase
|
|
end
|
|
|
|
/* Restore LastKey() */
|
|
SetLastKey( nLastKey )
|
|
return nil
|
|
|
|
METHOD Hide() CLASS TDebugger
|
|
|
|
RestScreen( ,,,, ::cAppImage )
|
|
::cAppImage := nil
|
|
SetColor( ::cAppColors )
|
|
SetCursor( ::nAppCursor )
|
|
|
|
return nil
|
|
|
|
|
|
METHOD LoadColors() CLASS TDebugger
|
|
|
|
LOCAL n
|
|
|
|
::oPullDown:LoadColors()
|
|
IF ::lActive
|
|
::oPullDown:Refresh()
|
|
::BarDisplay()
|
|
ENDIF
|
|
for n := 1 to Len( ::aWindows )
|
|
::aWindows[ n ]:LoadColors()
|
|
IF ::lActive
|
|
::aWindows[ n ]:Refresh()
|
|
ENDIF
|
|
next
|
|
|
|
RETURN NIL
|
|
|
|
|
|
METHOD MonoDisplay() CLASS TDebugger
|
|
|
|
::lMonoDisplay := ! ::lMonoDisplay
|
|
::oPullDown:GetItemByIdent( "MONO" ):checked := ::lMonoDisplay
|
|
::LoadColors()
|
|
|
|
return nil
|
|
|
|
|
|
METHOD NextWindow() CLASS TDebugger
|
|
|
|
local oWnd
|
|
|
|
if Len( ::aWindows ) > 0
|
|
oWnd := ::aWindows[ ::nCurrentWindow++ ]
|
|
oWnd:SetFocus( .f. )
|
|
if ::nCurrentWindow > Len( ::aWindows )
|
|
::nCurrentWindow := 1
|
|
endif
|
|
while ! ::aWindows[ ::nCurrentWindow ]:lVisible
|
|
::nCurrentWindow++
|
|
if ::nCurrentWindow > Len( ::aWindows )
|
|
::nCurrentWindow := 1
|
|
endif
|
|
end
|
|
oWnd := ::aWindows[ ::nCurrentWindow ]
|
|
oWnd:SetFocus( .t. )
|
|
endif
|
|
|
|
return nil
|
|
|
|
|
|
METHOD PathForFiles( cPathForFiles ) CLASS TDebugger
|
|
|
|
IF cPathForFiles == NIL
|
|
cPathForFiles := ::InputBox( "Search path for source files:", ::cPathForFiles )
|
|
ENDIF
|
|
IF ! RIGHT(cPathForFiles, 1) $ HB_OSPATHDELIMITERS()
|
|
cPathForFiles += HB_OSPATHSEPARATOR()
|
|
ENDIF
|
|
::cPathForFiles := cPathForFiles
|
|
|
|
RETURN Self
|
|
|
|
|
|
METHOD PrevWindow() CLASS TDebugger
|
|
|
|
local oWnd
|
|
|
|
if Len( ::aWindows ) > 0
|
|
oWnd := ::aWindows[ ::nCurrentWindow-- ]
|
|
oWnd:SetFocus( .f. )
|
|
if ::nCurrentWindow < 1
|
|
::nCurrentWindow := Len( ::aWindows )
|
|
endif
|
|
while ! ::aWindows[ ::nCurrentWindow ]:lVisible
|
|
::nCurrentWindow--
|
|
if ::nCurrentWindow < 1
|
|
::nCurrentWindow := Len( ::aWindows )
|
|
endif
|
|
end
|
|
oWnd := ::aWindows[ ::nCurrentWindow ]
|
|
oWnd:SetFocus( .t. )
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD Show() CLASS TDebugger
|
|
|
|
::cAppImage := SaveScreen()
|
|
::nAppRow := Row()
|
|
::nAppCol := Col()
|
|
::cAppColors := SetColor()
|
|
::nAppCursor := SetCursor( SC_NONE )
|
|
|
|
::oPullDown:Display()
|
|
::oWndCode:Show( .t. )
|
|
::oWndCommand:Show()
|
|
DispOutAt( ::oWndCommand:nBottom - 1, ::oWndCommand:nLeft + 1, ">" )
|
|
|
|
::BarDisplay()
|
|
|
|
return nil
|
|
|
|
METHOD ShowAppScreen() CLASS TDebugger
|
|
|
|
::cImage := SaveScreen()
|
|
RestScreen( 0, 0, MaxRow(), MaxCol(), ::cAppImage )
|
|
|
|
if LastKey() == K_LBUTTONDOWN
|
|
InKey( 0, INKEY_ALL )
|
|
InKey( 0, INKEY_ALL )
|
|
else
|
|
InKey( 0, INKEY_ALL )
|
|
endif
|
|
|
|
while LastKey() == K_MOUSEMOVE
|
|
InKey( 0, INKEY_ALL )
|
|
end
|
|
RestScreen( 0, 0, MaxRow(), MaxCol(), ::cImage )
|
|
|
|
return nil
|
|
|
|
METHOD ShowCallStack() CLASS TDebugger
|
|
|
|
local n := 1
|
|
local oCol
|
|
LOCAL i
|
|
|
|
::lShowCallStack = .t.
|
|
|
|
if ::oWndStack == nil
|
|
|
|
SetCursor( SC_NONE )
|
|
|
|
DispBegin()
|
|
// Resize code window
|
|
::oWndCode:Resize(,,,::oWndCode:nRight-16)
|
|
// Resize vars window
|
|
if ::oWndVars != nil
|
|
::oWndVars:Resize(,,, ::oWndVars:nRight - 16 )
|
|
endif
|
|
// Resize watchpoints window
|
|
if ::oWndPnt != nil
|
|
::oWndPnt:Resize(,,, ::oWndPnt:nRight - 16 )
|
|
endif
|
|
DispEnd()
|
|
|
|
if ::aWindows[ ::nCurrentWindow ]:lFocused
|
|
::aWindows[ ::nCurrentWindow ]:SetFocus( .f. )
|
|
endif
|
|
|
|
::oWndStack := TDbWindow():New( 1, MaxCol() - 15, MaxRow() - 6, MaxCol(),;
|
|
"Calls" )
|
|
::oWndStack:bKeyPressed := { | nKey | ::CallStackProcessKey( nKey ) }
|
|
::oWndStack:bLButtonDown := { | nKey | ::CallStackProcessKey( K_LBUTTONDOWN ) }
|
|
|
|
AAdd( ::aWindows, ::oWndStack )
|
|
// ::nCurrentWindow = Len( ::aWindows )
|
|
|
|
if ::oBrwStack == nil
|
|
::BuildBrowseStack()
|
|
endif
|
|
|
|
::oWndStack:bPainted := { || ::oBrwStack:ColorSpec := __DbgColors()[ 2 ] + "," + ;
|
|
__DbgColors()[ 5 ] + "," + __DbgColors()[ 4 ],;
|
|
::oBrwStack:RefreshAll(), ::oBrwStack:ForceStable() }
|
|
::oWndStack:bGotFocus = { || SetCursor( SC_NONE ) }
|
|
|
|
::oWndStack:Show( .f. )
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD LoadCallStack() CLASS TDebugger
|
|
LOCAL i
|
|
LOCAL nDebugLevel
|
|
LOCAL nCurrLevel
|
|
LOCAL nlevel, nPos
|
|
|
|
::aProcStack := ARRAY( ::nProcLevel )
|
|
nCurrLevel := hb_dbg_ProcLevel() - 1
|
|
nDebugLevel := nCurrLevel - ::nProcLevel +1
|
|
FOR i:=nDebugLevel TO nCurrLevel
|
|
nLevel := nCurrLevel - i +1
|
|
nPos := ASCAN( ::aCallStack, {|a| a[CSTACK_LEVEL]==nLevel} )
|
|
IF( nPos > 0 )
|
|
//a procedure with debug info
|
|
::aProcStack[i-nDebugLevel+1] := ::aCallStack[ nPos ]
|
|
ELSE
|
|
::aProcStack[i-nDebugLevel+1] := { PROCNAME( i )+"("+NTRIM(PROCLINE(i))+")", ,,,, nLevel }
|
|
ENDIF
|
|
NEXT
|
|
|
|
RETURN NIL
|
|
|
|
METHOD LoadSettings() CLASS TDebugger
|
|
|
|
local cInfo := MemoRead( ::cSettingsFileName )
|
|
local n, cLine, nColor, nLen
|
|
|
|
nLen := MLCount( cInfo )
|
|
for n := 1 to nLen
|
|
cLine := MemoLine( cInfo, 120, n )
|
|
do case
|
|
case Upper( SubStr( cLine, 1, 14 ) ) == "OPTIONS COLORS"
|
|
cLine := SubStr( cLine, At( "{", cLine ) + 1 )
|
|
nColor := 1
|
|
while nColor < 12
|
|
if At( ",", cLine ) != 0
|
|
::aColors[ nColor ] := ;
|
|
StrTran( SubStr( cLine, 1, At( ",", cLine ) - 1 ), '"', "" )
|
|
cLine := SubStr( cLine, At( ",", cLine ) + 1 )
|
|
else
|
|
::aColors[ nColor ] := ;
|
|
StrTran( SubStr( cLine, 1, At( "}", cLine ) - 1 ), '"', "" )
|
|
endif
|
|
nColor++
|
|
end
|
|
|
|
case Upper( SubStr( cLine, 1, 11 ) ) == "OPTIONS TAB"
|
|
cLine = SubStr( cLine, 12, 3 )
|
|
::nTabWidth = Val( cLine )
|
|
|
|
case Upper( SubStr( cLine, 1, 12 ) ) == "OPTIONS PATH"
|
|
cLine = SubStr( cLine, 13, 120 )
|
|
::cPathForFiles = AllTrim( cLine )
|
|
|
|
case Upper( SubStr( cLine, 1, 22 ) ) == "OPTIONS NORUNATSTARTUP"
|
|
::lRunAtStartup := .F.
|
|
|
|
case Upper( SubStr( cLine, 1, 14 ) ) == "MONITOR STATIC"
|
|
::lShowStatics = .t.
|
|
|
|
case Upper( SubStr( cLine, 1, 14 ) ) == "MONITOR PUBLIC"
|
|
::lShowPublics = .t.
|
|
|
|
case Upper( SubStr( cLine, 1, 13 ) ) == "MONITOR LOCAL"
|
|
::lShowLocals = .t.
|
|
|
|
case Upper( SubStr( cLine, 1, 15 ) ) == "MONITOR PRIVATE"
|
|
::lShowPrivates = .t.
|
|
|
|
case Upper( SubStr( cLine, 1, 12 ) ) == "MONITOR SORT"
|
|
::lSortVars = .t.
|
|
|
|
case Upper( SubStr( cLine, 1, 14 ) ) == "VIEW CALLSTACK"
|
|
::lShowCallStack = .t.
|
|
|
|
case Upper( SubStr( cLine, 1, 2 ) ) == "BP"
|
|
AAdd( ::aBreakPoints,;
|
|
{ Val( SubStr( cLine, 4, RAt( " ", cLine ) - 4 ) ),;
|
|
SubStr( cLine, RAt( " ", cLine ) ) } )
|
|
|
|
endcase
|
|
next
|
|
|
|
return nil
|
|
|
|
METHOD LoadVars() CLASS TDebugger // updates monitored variables
|
|
|
|
local nCount, n, m, xValue, cName
|
|
local cStaticName, nStaticIndex, nStaticsBase
|
|
LOCAL aVars
|
|
LOCAL aBVars
|
|
|
|
aBVars := {}
|
|
|
|
if ::lShowPublics
|
|
nCount := __mvDbgInfo( HB_MV_PUBLIC )
|
|
for n := nCount to 1 step -1
|
|
xValue := __mvDbgInfo( HB_MV_PUBLIC, n, @cName )
|
|
if cName != "__DBGSTATICS" // reserved public used by the debugger
|
|
AAdd( aBVars, { cName, xValue, "Public" } )
|
|
endif
|
|
next
|
|
endif
|
|
|
|
if ::lShowPrivates
|
|
nCount := __mvDbgInfo( HB_MV_PRIVATE )
|
|
for n := nCount to 1 step -1
|
|
xValue := __mvDbgInfo( HB_MV_PRIVATE, n, @cName )
|
|
AAdd( aBVars, { cName, xValue, "Private" } )
|
|
next
|
|
endif
|
|
|
|
IF( ::aProcStack[::oBrwStack:Cargo ][ CSTACK_LINE ] != nil )
|
|
if ::lShowStatics
|
|
if Type( "__DbgStatics" ) == "A"
|
|
cName := ::aProcStack[ ::oBrwStack:Cargo ][ CSTACK_MODULE ]
|
|
n := ASCAN( __dbgStatics, {|a| a[1]==cName} )
|
|
IF( n > 0 )
|
|
aVars := __DbgStatics[ n ][ 2 ]
|
|
for m := 1 to Len( aVars )
|
|
AAdd( aBVars, aVars[ m ] )
|
|
next
|
|
ENDIF
|
|
aVars := ::aProcStack[ ::oBrwStack:Cargo ][ CSTACK_STATICS ]
|
|
for n := 1 to Len( aVars )
|
|
AAdd( aBVars, aVars[ n ] )
|
|
next
|
|
endif
|
|
endif
|
|
|
|
if ::lShowLocals
|
|
aVars := ::aProcStack[ ::oBrwStack:Cargo ][ CSTACK_LOCALS ]
|
|
for n := 1 to Len( aVars )
|
|
cName := aVars[ n ][ VAR_NAME ]
|
|
m := AScan( aBVars,; // Is there another var with this name ?
|
|
{ | aVar | aVar[ VAR_NAME ] == cName .AND. LEFT(aVar[VAR_TYPE],1)=='S'} )
|
|
IF( m > 0 )
|
|
aBVars[ m ] := aVars[ n ]
|
|
ELSE
|
|
AAdd( aBVars, aVars[ n ] )
|
|
ENDIF
|
|
next
|
|
endif
|
|
ENDIF
|
|
|
|
IF( ::oBrwVars != NIL .AND. ::oBrwVars:cargo[1] > LEN(aBVars) )
|
|
::oBrwVars:gotop()
|
|
ENDIF
|
|
::aVars := aBVars
|
|
if ::lSortVars
|
|
::Sort()
|
|
endif
|
|
|
|
return nil
|
|
|
|
|
|
METHOD RefreshVars() CLASS TDebugger
|
|
::oPulldown:GetItemByIdent( "LOCAL" ):checked := ::lShowLocals
|
|
::oPulldown:GetItemByIdent( "PRIVATE" ):checked := ::lShowPrivates
|
|
::oPulldown:GetItemByIdent( "PUBLIC" ):checked := ::lShowPublics
|
|
::oPulldown:GetItemByIdent( "STATIC" ):checked := ::lShowStatics
|
|
::oPulldown:GetItemByIdent( "ALL" ):checked := ::lAll
|
|
IF ::lActive
|
|
if ::lShowPublics .or. ::lShowPrivates .or. ::lShowStatics .or. ::lShowLocals
|
|
::LoadVars()
|
|
::ShowVars()
|
|
else
|
|
::HideVars()
|
|
endif
|
|
ENDIF
|
|
RETURN NIL
|
|
|
|
|
|
METHOD ShowHelp( nTopic ) CLASS TDebugger
|
|
|
|
local nCursor := SetCursor( SC_NONE )
|
|
|
|
__dbgHelp( nTopic )
|
|
SetCursor( nCursor )
|
|
|
|
return nil
|
|
|
|
METHOD ShowVars() CLASS TDebugger
|
|
|
|
local nWidth, n := 1
|
|
Local oCol
|
|
local lRepaint := .f.
|
|
local nTop
|
|
|
|
if ::lGo
|
|
return nil
|
|
endif
|
|
|
|
if ! ( ::lShowLocals .or. ::lShowStatics .or. ::lShowPrivates .or. ;
|
|
::lShowPublics )
|
|
return nil
|
|
endif
|
|
|
|
if ::oWndVars == nil
|
|
|
|
nTop := IIF(::oWndPnt!=NIL .AND. ::oWndPnt:lVisible,::oWndPnt:nBottom+1,1)
|
|
|
|
::oWndVars := TDbWindow():New( nTop, 0, nTop+Min( 5, Len( ::aVars )+1 ),;
|
|
MaxCol() - iif( ::oWndStack != nil, ::oWndStack:nWidth(), 0 ),;
|
|
"Monitor:" + iif( ::lShowLocals, " Local", "" ) + ;
|
|
iif( ::lShowStatics, " Static", "" ) + iif( ::lShowPrivates, " Private", "" ) + ;
|
|
iif( ::lShowPublics, " Public", "" ) )
|
|
|
|
::oWndVars:bLButtonDown := { | nMRow, nMCol | ::WndVarsLButtonDown( nMRow, nMCol ) }
|
|
::oWndVars:bLDblClick := { | nMRow, nMCol | ::EditVar( ::oBrwVars:Cargo[ 1 ] ) }
|
|
|
|
::oBrwVars := TDbgBrowser():New( nTop+1, 1, ::oWndVars:nBottom - 1, MaxCol() - iif( ::oWndStack != nil,;
|
|
::oWndStack:nWidth(), 0 ) - 1 )
|
|
|
|
::oWndVars:Browser := ::oBrwVars
|
|
|
|
::oBrwVars:Cargo :={ 1,{}} // Actual highligthed row
|
|
::oBrwVars:ColorSpec := ::aColors[ 2 ] + "," + ::aColors[ 5 ] + "," + ::aColors[ 3 ]
|
|
::oBrwVars:GOTOPBLOCK := { || ::oBrwVars:cargo[ 1 ] := Min( 1, Len( ::aVars ) ) }
|
|
::oBrwVars:GoBottomBlock := { || ::oBrwVars:cargo[ 1 ] := MAX(1,Len( ::aVars )) }
|
|
::oBrwVars:SkipBlock = { | nSkip, nOld | nOld := ::oBrwVars:Cargo[ 1 ],;
|
|
::oBrwVars:Cargo[ 1 ] += nSkip,;
|
|
::oBrwVars:Cargo[ 1 ] := Min( Max( ::oBrwVars:Cargo[ 1 ], 1 ),;
|
|
Len( ::aVars ) ),;
|
|
If( Len( ::aVars ) > 0, ::oBrwVars:Cargo[ 1 ] - nOld, 0 ) }
|
|
|
|
nWidth := ::oWndVars:nWidth() - 1
|
|
oCol:=TBColumnNew( "", ;
|
|
{ || PadR( If( Len( ::aVars ) > 0, ;
|
|
AllTrim( Str( ::oBrwVars:Cargo[1] -1 ) ) + ") " + ;
|
|
::VarGetInfo( ::aVars[ Max( ::oBrwVars:Cargo[1], 1 ) ] ), ;
|
|
" " ), ;
|
|
::oWndVars:nWidth() - 2 ) } )
|
|
::oBrwVars:AddColumn( oCol )
|
|
AAdd(::oBrwVars:Cargo[2],::aVars)
|
|
oCol:DefColor:={1,2}
|
|
if Len( ::aVars ) > 0
|
|
::oBrwVars:ForceStable()
|
|
endif
|
|
|
|
::oWndVars:bPainted := { || if(Len( ::aVars ) > 0, ( ::obrwVars:refreshAll():ForceStable(),RefreshVarsS(::oBrwVars) ),) }
|
|
|
|
::oWndVars:bKeyPressed := { | nKey | ( iif( nKey == K_DOWN ;
|
|
, ::oBrwVars:Down(), nil ), iif( nKey == K_UP, ::oBrwVars:Up(), nil ) ;
|
|
, iif( nKey == K_PGDN, ::oBrwVars:PageDown(), nil ) ;
|
|
, iif( nKey == K_PGUP, ::oBrwVars:PageUp(), nil ) ;
|
|
, iif( nKey == K_HOME, ::oBrwVars:GoTop(), nil ) ;
|
|
, iif( nKey == K_END, ::oBrwVars:GoBottom(), nil ) ;
|
|
, iif( nKey == K_ENTER, ::EditVar( ::oBrwVars:Cargo[1] ), nil ), IIF(LEN(::aVars)>0,::oBrwVars:ForceStable(),nil) ) }
|
|
|
|
AAdd( ::aWindows, ::oWndVars )
|
|
::oWndVars:Show()
|
|
::ResizeWindows( ::oWndVars )
|
|
|
|
else
|
|
::oWndVars:cCaption := "Monitor:" + ;
|
|
iif( ::lShowLocals, " Local", "" ) + ;
|
|
iif( ::lShowStatics, " Static", "" ) + ;
|
|
iif( ::lShowPrivates, " Private", "" ) + ;
|
|
iif( ::lShowPublics, " Public", "" )
|
|
|
|
DispBegin()
|
|
if( ::oBrwVars:cargo[1] <= 0 )
|
|
::oBrwVars:cargo[1] := 1
|
|
endif
|
|
|
|
if Len( ::aVars ) == 0
|
|
if ::oWndVars:nBottom - ::oWndVars:nTop > 1
|
|
::oWndVars:Resize( ,, ::oWndVars:nTop + 1 )
|
|
lRepaint := .t.
|
|
else
|
|
/* We still need to redraw window caption, it could have changed */
|
|
::oWndVars:Refresh()
|
|
endif
|
|
elseif Len( ::aVars ) > ::oWndVars:nBottom - ::oWndVars:nTop - 1
|
|
::oWndVars:Resize( ,, ::oWndVars:nTop + Min( Len( ::aVars ) + 1, 7 ) )
|
|
lRepaint := .t.
|
|
elseif Len( ::aVars ) < ::oWndVars:nBottom - ::oWndVars:nTop - 1
|
|
::oWndVars:Resize( ,, ::oWndVars:nTop + Len( ::aVars ) + 1 )
|
|
lRepaint := .t.
|
|
else
|
|
::oBrwVars:RefreshAll():ForceStable()
|
|
::oWndVars:Refresh()
|
|
endif
|
|
if ! ::oWndVars:lVisible .OR. lRepaint
|
|
::ResizeWindows( ::oWndVars )
|
|
endif
|
|
DispEnd()
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD VarGetInfo( aVar ) CLASS TDebugger
|
|
LOCAL uValue
|
|
LOCAL cType:=LEFT(aVar[VAR_TYPE],1)
|
|
|
|
uValue := ::VarGetValue( aVar )
|
|
do case
|
|
case cType== "L"
|
|
return aVar[ VAR_NAME ] + " <Local, " + ;
|
|
ValType( uValue ) + ;
|
|
">: " + ValToStr( uValue )
|
|
|
|
case cType== "S"
|
|
return aVar[ VAR_NAME ] + " <Static, " + ;
|
|
ValType( uValue ) + ;
|
|
">: " + ValToStr( uValue )
|
|
|
|
OTHERWISE
|
|
return aVar[ VAR_NAME ] + " <" + aVar[ VAR_TYPE ] + ", " + ;
|
|
ValType( uValue ) + ;
|
|
">: " + ValToStr( uValue )
|
|
|
|
endcase
|
|
|
|
return ""
|
|
|
|
static function CompareLine( Self )
|
|
|
|
return { | a | a[ 1 ] == Self:oBrwText:nRow } // it was nLine
|
|
|
|
|
|
METHOD StackProc( cModuleName, nProcLevel ) CLASS TDebugger
|
|
// always treat filename as lower case - we need it consistent for comparisons
|
|
LOCAL nPos:=RAT( ":", cModuleName )
|
|
LOCAL aEntry := { ;
|
|
IIF(::lCodeBlock,"(b)","")+SubStr( cModuleName, nPos + 1 ),; //function name
|
|
{},; //local vars
|
|
nil,; //line no, nil means that no line number is stored yet
|
|
lower( strip_path( LEFT( cModuleName, nPos - 1 ) ) ),; // and the module name
|
|
{}, ; // static vars
|
|
nProcLevel }
|
|
|
|
ASize( ::aCallStack, Len( ::aCallStack ) + 1 )
|
|
AIns( ::aCallStack, 1 )
|
|
::aCallStack[ 1 ] := aEntry
|
|
return nil
|
|
|
|
|
|
//METHOD ShowCodeLine( nLine, cPrgName ) CLASS TDebugger
|
|
METHOD ShowCodeLine( nProc ) CLASS TDebugger
|
|
LOCAL nPos, nLevel
|
|
LOCAL nLine, cPrgName
|
|
|
|
// we only update the stack window and up a new browse
|
|
// to view the code if we have just broken execution
|
|
if !::lGo .AND. !::lTrace
|
|
if ::oWndStack != nil
|
|
::oBrwStack:RefreshAll()
|
|
endif
|
|
|
|
|
|
nLine := ::aProcStack[ nProc ][ CSTACK_LINE ]
|
|
cPrgName := ::aProcStack[ nProc ][ CSTACK_MODULE ]
|
|
IF( nLine == NIL )
|
|
::oBrwText := nil
|
|
::oWndCode:Browser := nil
|
|
::oWndCode:SetCaption( ::aProcStack[ nProc ][ CSTACK_FUNCTION ] +;
|
|
": Code not available" )
|
|
::oWndCode:Refresh() // to force the window caption to update
|
|
RETURN nil
|
|
ENDIF
|
|
|
|
if( ::lppo )
|
|
nPos :=RAT(".PRG", UPPER(cPrgName) )
|
|
IF( nPos > 0 )
|
|
cPrgName := LEFT( cPrgName, nPos-1 ) + ".ppo"
|
|
ELSE
|
|
cPrgName += cPrgName +".ppo"
|
|
ENDIF
|
|
endif
|
|
|
|
if ! empty( cPrgName )
|
|
|
|
if ( strip_path( cPrgName ) != strip_path( ::cPrgName ) .OR. ::oBrwText == NIL )
|
|
|
|
if ! File( cPrgName ) .and. !Empty( ::cPathForFiles )
|
|
cPrgName := ::LocatePrgPath( cPrgName )
|
|
endif
|
|
|
|
::cPrgName := cPrgName
|
|
|
|
if ::oBrwText == nil
|
|
::oBrwText := TBrwText():New( ::oWndCode:nTop + 1, ::oWndCode:nLeft + 1,;
|
|
::oWndCode:nBottom - 1, ::oWndCode:nRight - 1, cPrgName,;
|
|
__DbgColors()[ 2 ] + "," + __DbgColors()[ 5 ] + "," + ;
|
|
__DbgColors()[ 3 ] + "," + __DbgColors()[ 6 ], ;
|
|
::lLineNumbers )
|
|
|
|
::oWndCode:Browser := ::oBrwText
|
|
|
|
else
|
|
::oBrwText:LoadFile(cPrgName)
|
|
endif
|
|
|
|
::oWndCode:bPainted := {|| IIF( ::oBrwText != nil, ::oBrwText:RefreshAll():ForceStable(), ::oWndCode:Clear() ) }
|
|
::RedisplayBreakpoints() // check for breakpoints in this file and display them
|
|
::oWndCode:SetCaption( ::cPrgName )
|
|
::oWndCode:Refresh() // to force the window caption to update
|
|
endif
|
|
::oBrwText:SetActiveLine( nLine )
|
|
::GotoLine( nLine )
|
|
endif
|
|
endif
|
|
|
|
return nil
|
|
|
|
|
|
METHOD Open() CLASS TDebugger
|
|
LOCAL cFileName := ::InputBox( "Please enter the filename", Space( 255 ) )
|
|
LOCAL cPrgName
|
|
|
|
cFileName:= ALLTRIM( cFileName )
|
|
|
|
if !EMPTY(cFileName) .AND. (cFileName != ::cPrgName .OR. valtype(::cPrgName)=='U')
|
|
if ! File( cFileName ) .and. ! Empty( ::cPathForFiles )
|
|
cFileName := ::LocatePrgPath( cFileName )
|
|
if Empty( cFileName )
|
|
Alert( "File not found!" )
|
|
return NIL
|
|
endif
|
|
endif
|
|
::cPrgName := cFileName
|
|
::lppo := RAT(".PPO", UPPER(cFileName)) > 0
|
|
::oPulldown:GetItemByIdent( "PPO" ):Checked := ::lppo
|
|
::oBrwText := nil
|
|
::oBrwText := TBrwText():New( ::oWndCode:nTop + 1, ::oWndCode:nLeft + 1,;
|
|
::oWndCode:nBottom - 1, ::oWndCode:nRight - 1, cFileName,;
|
|
__DbgColors()[ 2 ] + "," + __DbgColors()[ 5 ] + "," + ;
|
|
__DbgColors()[ 3 ] + "," + __DbgColors()[ 6 ], ;
|
|
::lLineNumbers )
|
|
::oWndCode:Browser := ::oBrwText
|
|
::RedisplayBreakpoints() // check for breakpoints in this file and display them
|
|
::oWndCode:SetCaption( ::cPrgName )
|
|
::oWndCode:Refresh() // to force the window caption to update
|
|
endif
|
|
return nil
|
|
|
|
METHOD OpenPPO() CLASS TDebugger
|
|
LOCAL nPos
|
|
LOCAL lSuccess:=.F.
|
|
|
|
nPos := RAT(".PPO", UPPER(::cPrgName))
|
|
IF( nPos == 0 )
|
|
nPos := RAT(".PRG", UPPER(::cPrgName))
|
|
IF( nPos > 0 )
|
|
::cPrgName := LEFT(::cPrgName,nPos-1) + ".ppo"
|
|
ELSE
|
|
::cPrgName += ".ppo"
|
|
ENDIF
|
|
lSuccess := FILE(::cPrgName)
|
|
::lppo := lSuccess
|
|
ELSE
|
|
::cPrgName := LEFT(::cPrgName,nPos-1) + ".prg"
|
|
lSuccess := FILE( ::cPrgName )
|
|
::lppo := !lSuccess
|
|
ENDIF
|
|
|
|
IF( lSuccess )
|
|
::oBrwText := nil
|
|
::oBrwText := TBrwText():New( ::oWndCode:nTop + 1, ::oWndCode:nLeft + 1,;
|
|
::oWndCode:nBottom - 1, ::oWndCode:nRight - 1, ::cPrgName,;
|
|
__DbgColors()[ 2 ] + "," + __DbgColors()[ 5 ] + "," + ;
|
|
__DbgColors()[ 3 ] + "," + __DbgColors()[ 6 ], ::lLineNumbers )
|
|
::oWndCode:Browser := ::oBrwText
|
|
::RedisplayBreakpoints() // check for breakpoints in this file and display them
|
|
::oWndCode:SetCaption( ::cPrgName )
|
|
::oWndCode:Refresh()// to force the window caption to update
|
|
endif
|
|
|
|
::oPullDown:GetItemByIdent( "PPO" ):checked := ::lPPO
|
|
|
|
return lSuccess
|
|
|
|
|
|
// check for breakpoints in the current file and display them
|
|
METHOD RedisplayBreakPoints() CLASS TDebugger
|
|
|
|
local n
|
|
for n := 1 to Len( ::aBreakpoints )
|
|
if ::aBreakpoints[ n ] [ 2 ] == strip_path( ::cPrgName )
|
|
::oBrwText:ToggleBreakPoint(::aBreakpoints[ n ] [ 1 ], .T.)
|
|
Endif
|
|
next
|
|
return nil
|
|
|
|
METHOD OSShell() CLASS TDebugger
|
|
|
|
local cImage := SaveScreen()
|
|
local cColors := SetColor()
|
|
local cOs := Upper( OS() )
|
|
local cShell
|
|
local bLastHandler := ErrorBlock({ |objErr| BREAK (objErr) })
|
|
local oE
|
|
|
|
SET COLOR TO "W/N"
|
|
CLS
|
|
? "Type 'exit' to return to the Debugger"
|
|
SetCursor( SC_NORMAL )
|
|
|
|
begin sequence
|
|
if At("WINDOWS", cOs) != 0 .OR. At("DOS", cOs) != 0 .OR. At("OS/2", cOs) != 0
|
|
cShell := GetEnv("COMSPEC")
|
|
RUN ( cShell )
|
|
elseif At("LINUX", cOs) != 0
|
|
cShell := GetEnv("SHELL")
|
|
RUN ( cShell )
|
|
else
|
|
Alert( "Not implemented yet!" )
|
|
endif
|
|
|
|
recover using oE
|
|
Alert("Error: " + oE:description)
|
|
|
|
end sequence
|
|
|
|
ErrorBlock(bLastHandler)
|
|
|
|
SetCursor( SC_NONE )
|
|
RestScreen( ,,,, cImage )
|
|
SetColor( cColors )
|
|
|
|
return nil
|
|
|
|
METHOD HideCallStack() CLASS TDebugger
|
|
|
|
::lShowCallStack = .f.
|
|
|
|
if ::oWndStack != nil
|
|
DispBegin()
|
|
::oWndStack:Hide()
|
|
if ::aWindows[ ::nCurrentWindow ] == ::oWndStack
|
|
::NextWindow()
|
|
ENDIF
|
|
::RemoveWindow( ::oWndStack )
|
|
::oWndStack = nil
|
|
|
|
::oWndCode:Resize(,,, ::oWndCode:nRight + 16 )
|
|
if ::oWndVars != nil
|
|
::oWndVars:Resize(,,, ::oWndVars:nRight + 16 )
|
|
endif
|
|
if ::oWndPnt != nil
|
|
::oWndPnt:Resize(,,, ::oWndPnt:nRight + 16 )
|
|
endif
|
|
DispEnd()
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD HideVars() CLASS TDebugger
|
|
|
|
::oWndVars:Hide()
|
|
::oWndCode:nTop := 1
|
|
::oBrwText:Resize( ::oWndCode:nTop+1 )
|
|
if ::aWindows[ ::nCurrentWindow ] == ::oWndVars
|
|
::NextWindow()
|
|
ENDIF
|
|
|
|
return nil
|
|
|
|
METHOD InputBox( cMsg, uValue, bValid, lEditable ) CLASS TDebugger
|
|
|
|
local nTop := ( MaxRow() / 2 ) - 5
|
|
local nLeft := ( MaxCol() / 2 ) - 25
|
|
local nBottom := ( MaxRow() / 2 ) - 3
|
|
local nRight := ( MaxCol() / 2 ) + 25
|
|
local cType := ValType( uValue )
|
|
local uTemp := PadR( uValue, nRight - nLeft - 1 )
|
|
local GetList := {}
|
|
local nOldCursor
|
|
local lScoreBoard := Set( _SET_SCOREBOARD, .f. )
|
|
local lExit
|
|
local oWndInput := TDbWindow():New( nTop, nLeft, nBottom, nRight, cMsg,;
|
|
::oPullDown:cClrPopup )
|
|
|
|
DEFAULT lEditable TO .t.
|
|
|
|
oWndInput:lShadow := .t.
|
|
oWndInput:Show()
|
|
|
|
if lEditable
|
|
if bValid == nil
|
|
@ nTop + 1, nLeft + 1 GET uTemp COLOR "," + __DbgColors()[ 5 ]
|
|
else
|
|
@ nTop + 1, nLeft + 1 GET uTemp VALID Eval( bValid, uTemp ) ;
|
|
COLOR "," + __DbgColors()[ 5 ]
|
|
endif
|
|
|
|
nOldCursor := SetCursor( SC_NORMAL )
|
|
READ
|
|
SetCursor( nOldCursor )
|
|
else
|
|
@ nTop + 1, nLeft + 1 SAY ValToStr( uValue ) COLOR "," + __DbgColors()[ 5 ]
|
|
SetPos( nTop + 1, nLeft + 1 )
|
|
nOldCursor := SetCursor( SC_NONE )
|
|
|
|
lExit = .f.
|
|
|
|
while ! lExit
|
|
Inkey( 0 )
|
|
|
|
do case
|
|
case LastKey() == K_ESC
|
|
lExit = .t.
|
|
|
|
case LastKey() == K_ENTER
|
|
if cType == "A"
|
|
if Len( uValue ) == 0
|
|
Alert( "Array is empty" )
|
|
else
|
|
__DbgArrays( uValue, cMsg )
|
|
endif
|
|
|
|
elseif cType == "O"
|
|
__DbgObject( uValue, cMsg )
|
|
|
|
else
|
|
Alert( "Value cannot be edited" )
|
|
endif
|
|
|
|
otherwise
|
|
Alert( "Value cannot be edited" )
|
|
endcase
|
|
end
|
|
|
|
SetCursor( nOldCursor )
|
|
endif
|
|
|
|
nOldCursor := SetCursor( SC_NORMAL )
|
|
READ
|
|
SetCursor( nOldCursor )
|
|
oWndInput:Hide()
|
|
Set( _SET_SCOREBOARD, lScoreBoard )
|
|
|
|
do case
|
|
case cType == "C"
|
|
uTemp := AllTrim( uTemp )
|
|
|
|
case cType == "D"
|
|
uTemp := CToD( uTemp )
|
|
|
|
case cType == "N"
|
|
uTemp := Val( uTemp )
|
|
|
|
endcase
|
|
|
|
return iif( LastKey() != K_ESC, uTemp, uValue )
|
|
|
|
METHOD Inspect( uValue, cValueName ) CLASS TDebugger
|
|
|
|
uValue = ::InputBox( uValue, cValueName,, .f. )
|
|
|
|
return nil
|
|
|
|
METHOD IsBreakPoint( nLine, cPrgName ) CLASS TDebugger
|
|
return AScan( ::aBreakPoints, { | aBreak | (aBreak[ 1 ] == nLine) .AND. (aBreak [ 2 ] == cPrgName) } ) != 0
|
|
|
|
|
|
METHOD GotoLine( nLine ) CLASS TDebugger
|
|
|
|
local nRow, nCol
|
|
/*
|
|
if ::oBrwVars != nil
|
|
::ShowVars()
|
|
endif
|
|
*/
|
|
::oBrwText:GotoLine( nLine )
|
|
nRow = Row()
|
|
nCol = Col()
|
|
|
|
// no source code line stored yet
|
|
/*
|
|
if ::oBrwStack != nil .and. Len( ::aCallStack ) > 0 .and. ;
|
|
::aCallStack[ ::oBrwStack:Cargo ][ CSTACK_LINE ] == nil
|
|
::aCallStack[ ::oBrwStack:Cargo ][ CSTACK_LINE ] = nLine
|
|
endif
|
|
*/
|
|
if ::oWndStack != nil .and. ! ::oBrwStack:Stable
|
|
::oBrwStack:ForceStable()
|
|
endif
|
|
|
|
if ::oWndCode:lFocused .and. SetCursor() != SC_SPECIAL1
|
|
SetPos( nRow, nCol )
|
|
SetCursor( SC_SPECIAL1 )
|
|
endif
|
|
SetPos( nRow, nCol )
|
|
|
|
// Store cursor position to be restored by ::oWndCode:bGotFocus
|
|
::oWndCode:cargo[ 1 ] := nRow
|
|
::oWndCode:cargo[ 2 ] := nCol
|
|
|
|
return nil
|
|
|
|
|
|
METHOD Local() CLASS TDebugger
|
|
|
|
::lShowLocals := ! ::lShowLocals
|
|
::RefreshVars()
|
|
|
|
return nil
|
|
|
|
|
|
METHOD Private() CLASS TDebugger
|
|
|
|
::lShowPrivates := ! ::lShowPrivates
|
|
::RefreshVars()
|
|
|
|
return nil
|
|
|
|
METHOD Public() CLASS TDebugger
|
|
|
|
::lShowPublics := ! ::lShowPublics
|
|
::RefreshVars()
|
|
|
|
return nil
|
|
|
|
METHOD RestoreAppStatus() CLASS TDebugger
|
|
|
|
::cImage := SaveScreen()
|
|
DispBegin()
|
|
RestScreen( 0, 0, MaxRow(), MaxCol(), ::cAppImage )
|
|
SetPos( ::nAppRow, ::nAppCol )
|
|
SetColor( ::cAppColors )
|
|
SetCursor( ::nAppCursor )
|
|
DispEnd()
|
|
|
|
return nil
|
|
|
|
METHOD RestoreSettings() CLASS TDebugger
|
|
|
|
local n
|
|
|
|
::cSettingsFileName := ::InputBox( "File name", ::cSettingsFileName )
|
|
|
|
if LastKey() != K_ESC
|
|
::LoadSettings()
|
|
|
|
::oPullDown:LoadColors()
|
|
::oPullDown:Refresh()
|
|
::BarDisplay()
|
|
::ShowVars()
|
|
for n := 1 to Len( ::aWindows )
|
|
::aWindows[ n ]:LoadColors()
|
|
::aWindows[ n ]:Refresh()
|
|
next
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD SaveAppStatus() CLASS TDebugger
|
|
|
|
DispBegin()
|
|
::cAppImage := SaveScreen()
|
|
::nAppRow := Row()
|
|
::nAppCol := Col()
|
|
::cAppColors := SetColor()
|
|
::nAppCursor := SetCursor( SC_NONE )
|
|
RestScreen( 0, 0, MaxRow(), MaxCol(), ::cImage )
|
|
DispEnd()
|
|
|
|
return nil
|
|
|
|
METHOD SaveSettings() CLASS TDebugger
|
|
|
|
local cInfo := "", n, oWnd
|
|
|
|
::cSettingsFileName := ::InputBox( "File name", ::cSettingsFileName )
|
|
|
|
if LastKey() != K_ESC
|
|
|
|
if ! Empty( ::cPathForFiles )
|
|
cInfo += "Options Path " + ::cPathForFiles + HB_OsNewLine()
|
|
endif
|
|
|
|
cInfo += "Options Colors {"
|
|
for n := 1 to Len( ::aColors )
|
|
cInfo += '"' + ::aColors[ n ] + '"'
|
|
if n < Len( ::aColors )
|
|
cInfo += ","
|
|
endif
|
|
next
|
|
cInfo += "}" + HB_OsNewLine()
|
|
|
|
if ::lMonoDisplay
|
|
cInfo += "Options mono " + HB_OsNewLine()
|
|
endif
|
|
|
|
if !::lRunAtStartup
|
|
cInfo += "Options NoRunAtStartup " + HB_OsNewLine()
|
|
endif
|
|
|
|
if ::nSpeed != 0
|
|
cInfo += "Run Speed " + AllTrim( Str( ::nSpeed ) ) + HB_OsNewLine()
|
|
endif
|
|
|
|
for n := 1 to Len( ::aWindows )
|
|
oWnd := ::aWindows[ n ]
|
|
cInfo += "Window Size " + AllTrim( Str( oWnd:nBottom - oWnd:nTop + 1 ) ) + " "
|
|
cInfo += AllTrim( Str( oWnd:nRight - oWnd:nLeft + 1 ) ) + HB_OsNewLine()
|
|
cInfo += "Window Move " + AllTrim( Str( oWnd:nTop ) ) + " "
|
|
cInfo += AllTrim( Str( oWnd:nLeft ) ) + HB_OsNewLine()
|
|
cInfo += "Window Next" + HB_OsNewLine()
|
|
next
|
|
|
|
if ::nTabWidth != 4
|
|
cInfo += "Options Tab " + AllTrim( Str( ::nTabWidth ) ) + HB_OsNewLine()
|
|
endif
|
|
|
|
if ::lShowStatics
|
|
cInfo += "Monitor Static" + HB_OsNewLine()
|
|
endif
|
|
|
|
if ::lShowPublics
|
|
cInfo += "Monitor Public" + HB_OsNewLine()
|
|
endif
|
|
|
|
if ::lShowLocals
|
|
cInfo += "Monitor Local" + HB_OsNewLine()
|
|
endif
|
|
|
|
if ::lShowPrivates
|
|
cInfo += "Monitor Private" + HB_OsNewLine()
|
|
endif
|
|
|
|
if ::lSortVars
|
|
cInfo += "Monitor Sort" + HB_OsNewLine()
|
|
endif
|
|
|
|
if ::lShowCallStack
|
|
cInfo += "View CallStack" + HB_OsNewLine()
|
|
endif
|
|
|
|
if ! Empty( ::aBreakPoints )
|
|
for n := 1 to Len( ::aBreakPoints )
|
|
cInfo += "BP " + AllTrim( Str( ::aBreakPoints[ n ][ 1 ] ) ) + " " + ;
|
|
AllTrim( ::aBreakPoints[ n ][ 2 ] ) + HB_OsNewLine()
|
|
next
|
|
endif
|
|
|
|
MemoWrit( ::cSettingsFileName, cInfo )
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD Stack() CLASS TDebugger
|
|
|
|
::lShowCallStack := ! ::lShowCallStack
|
|
::oPulldown:GetItemByIdent( "CALLSTACK" ):checked := ::lShowCallStack
|
|
if ::lActive
|
|
if ::lShowCallStack
|
|
::ShowCallStack()
|
|
else
|
|
::HideCallStack()
|
|
endif
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD Static() CLASS TDebugger
|
|
|
|
::lShowStatics := ! ::lShowStatics
|
|
::RefreshVars()
|
|
|
|
return nil
|
|
|
|
|
|
// Toggle a breakpoint at the cursor position in the currently viewed file
|
|
// which may be different from the file in which execution was broken
|
|
METHOD ToggleBreakPoint() CLASS TDebugger
|
|
// look for a breakpoint which matches both line number and program name
|
|
local nAt
|
|
LOCAL cLine
|
|
local cFileName
|
|
|
|
IF !::lActive
|
|
RETURN NIL
|
|
ENDIF
|
|
|
|
cLine := ::oBrwText:GetLine( ::oBrwText:nRow )
|
|
IF ::oBrwText:lLineNumbers
|
|
cLine := SUBSTR( cLine, AT(":",cLine)+1 )
|
|
ENDIF
|
|
IF IsValidStopLine( cLine )
|
|
cFileName := strip_path( ::cPrgName )
|
|
|
|
nAt := AScan( ::aBreakPoints, { | aBreak | aBreak[ 1 ] == ::oBrwText:nRow ;
|
|
.AND. aBreak[ 2 ] == cFileName } ) // it was nLine
|
|
|
|
if nAt == 0
|
|
AAdd( ::aBreakPoints, { ::oBrwText:nRow, cFileName } ) // it was nLine
|
|
::oBrwText:ToggleBreakPoint(::oBrwText:nRow, .T.)
|
|
else
|
|
ADel( ::aBreakPoints, nAt )
|
|
ASize( ::aBreakPoints, Len( ::aBreakPoints ) - 1 )
|
|
::oBrwText:ToggleBreakPoint(::oBrwText:nRow, .F.)
|
|
endif
|
|
|
|
::oBrwText:RefreshCurrent()
|
|
ENDIF
|
|
|
|
return nil
|
|
|
|
|
|
METHOD ViewSets() CLASS TDebugger
|
|
|
|
local oWndSets := TDbWindow():New( 1, 8, MaxRow() - 2, MaxCol() - 8,;
|
|
"System Settings[1..47]", ::ClrModal() )
|
|
local aSets := { "Exact", "Fixed", "Decimals", "DateFormat", "Epoch", "Path",;
|
|
"Default", "Exclusive", "SoftSeek", "Unique", "Deleted",;
|
|
"Cancel", "Debug", "TypeAhead", "Color", "Cursor", "Console",;
|
|
"Alternate", "AltFile", "Device", "Extra", "ExtraFile",;
|
|
"Printer", "PrintFile", "Margin", "Bell", "Confirm", "Escape",;
|
|
"Insert", "Exit", "Intensity", "ScoreBoard", "Delimeters",;
|
|
"DelimChars", "Wrap", "Message", "MCenter", "ScrollBreak",;
|
|
"EventMask", "VideoMode", "MBlockSize", "MFileExt",;
|
|
"StrictRead", "Optimize", "Autopen", "Autorder", "AutoShare" }
|
|
|
|
local oBrwSets := TBrowseNew( oWndSets:nTop + 1, oWndSets:nLeft + 1,;
|
|
oWndSets:nBottom - 1, oWndSets:nRight - 1 )
|
|
local n := 1
|
|
local nWidth := oWndSets:nRight - oWndSets:nLeft - 1
|
|
local oCol
|
|
oBrwSets:Cargo :={ 1,{}} // Actual highligthed row
|
|
oBrwSets:autolite:=.f.
|
|
oBrwSets:ColorSpec := ::ClrModal()
|
|
oBrwSets:GOTOPBLOCK := { || oBrwSets:cargo[ 1 ]:= 1 }
|
|
oBrwSets:GoBottomBlock := { || oBrwSets:cargo[ 1 ]:= Len(oBrwSets:cargo[ 2 ][ 1 ])}
|
|
oBrwSets:SKIPBLOCK := { |nPos| ( nPos:= ArrayBrowseSkip(nPos, oBrwSets), oBrwSets:cargo[ 1 ]:= ;
|
|
oBrwSets:cargo[ 1 ] + nPos,nPos ) }
|
|
oBrwSets:AddColumn( ocol := TBColumnNew( "", { || PadR( aSets[ oBrwSets:cargo[ 1 ] ], 12 ) } ) )
|
|
aadd(oBrwSets:Cargo[2],asets)
|
|
ocol:defcolor:={1,2}
|
|
oBrwSets:AddColumn( oCol := TBColumnNew( "",;
|
|
{ || PadR( ValToStr( Set( oBrwSets:cargo[ 1 ] ) ), nWidth - 13 ) } ) )
|
|
ocol:defcolor:={1,3}
|
|
ocol:width:=40
|
|
oWndSets:bPainted := { || oBrwSets:ForceStable(),RefreshVarsS(oBrwSets)}
|
|
oWndSets:bKeyPressed := { | nKey | SetsKeyPressed( nKey, oBrwSets, Len( aSets ),;
|
|
oWndSets, "System Settings",;
|
|
{ || ::EditSet( oBrwSets:Cargo[1], oBrwSets ) } ) }
|
|
|
|
SetCursor( SC_NONE )
|
|
oWndSets:ShowModal()
|
|
|
|
return nil
|
|
|
|
METHOD WndVarsLButtonDown( nMRow, nMCol ) CLASS TDebugger
|
|
|
|
if nMRow > ::oWndVars:nTop .and. ;
|
|
nMRow < ::oWndVars:nBottom .and. ;
|
|
nMCol > ::oWndVars:nLeft .and. ;
|
|
nMCol < ::oWndVars:nRight
|
|
if nMRow - ::oWndVars:nTop >= 1 .and. ;
|
|
nMRow - ::oWndVars:nTop <= Len( ::aVars )
|
|
while ::oBrwVars:RowPos > nMRow - ::oWndVars:nTop
|
|
::oBrwVars:Up()
|
|
::oBrwVars:ForceStable()
|
|
end
|
|
while ::oBrwVars:RowPos < nMRow - ::oWndVars:nTop
|
|
::oBrwVars:Down()
|
|
::oBrwVars:ForceStable()
|
|
end
|
|
endif
|
|
endif
|
|
|
|
return nil
|
|
|
|
static procedure SetsKeyPressed( nKey, oBrwSets, nSets, oWnd, cCaption, bEdit )
|
|
|
|
|
|
local nSet := oBrwSets:cargo[1]
|
|
local cTemp:=str(nSet,4)
|
|
|
|
Local nRectoMove
|
|
do case
|
|
case nKey == K_UP
|
|
oBrwSets:Up()
|
|
case nKey == K_DOWN
|
|
oBrwSets:Down()
|
|
case nKey == K_HOME .or. (nKey == K_CTRL_PGUP) .or. (nKey == K_CTRL_HOME)
|
|
oBrwSets:GoTop()
|
|
case nKey == K_END .or. (nkey == K_CTRL_PGDN) .or. (nkey == K_CTRL_END )
|
|
oBrwSets:GoBottom()
|
|
Case nKey == K_PGDN
|
|
oBrwSets:pageDown()
|
|
Case nKey == K_PGUP
|
|
OBrwSets:PageUp()
|
|
|
|
case nKey == K_ENTER
|
|
if bEdit != nil
|
|
Eval( bEdit )
|
|
endif
|
|
if LastKey() == K_ENTER
|
|
KEYBOARD Chr( K_DOWN )
|
|
endif
|
|
|
|
endcase
|
|
RefreshVarsS(oBrwSets)
|
|
|
|
oWnd:SetCaption( cCaption + "[" + AllTrim( Str( oBrwSets:Cargo[1] ) ) + ;
|
|
".." + AllTrim( Str( nSets ) ) + "]" )
|
|
|
|
return
|
|
|
|
static procedure SetsKeyVarPressed( nKey, oBrwSets, nSets, oWnd, bEdit )
|
|
Local nRectoMove
|
|
local nSet := oBrwSets:Cargo[1]
|
|
do case
|
|
case nKey == K_UP
|
|
oBrwSets:Up()
|
|
|
|
case nKey == K_DOWN
|
|
oBrwSets:Down()
|
|
|
|
case nKey == K_HOME .or. (nKey == K_CTRL_PGUP) .or. (nKey == K_CTRL_HOME)
|
|
oBrwSets:GoTop()
|
|
case nKey == K_END .or. (nkey == K_CTRL_PGDN) .or. (nkey == K_CTRL_END )
|
|
oBrwSets:GoBottom()
|
|
|
|
Case nKey == K_PGDN
|
|
oBrwSets:pageDown()
|
|
Case nKey == K_PGUP
|
|
OBrwSets:PageUp()
|
|
|
|
case nKey == K_ENTER
|
|
if bEdit != nil
|
|
Eval( bEdit )
|
|
endif
|
|
if LastKey() == K_ENTER
|
|
KEYBOARD Chr( K_DOWN )
|
|
endif
|
|
|
|
endcase
|
|
|
|
return
|
|
|
|
static function ValToStr( uVal )
|
|
|
|
local cType := ValType( uVal )
|
|
local cResult := "U"
|
|
|
|
do case
|
|
case uVal == nil
|
|
cResult := "NIL"
|
|
|
|
case cType == "A"
|
|
cResult := "{ ... }"
|
|
|
|
Case cType =="B"
|
|
cResult:= "{ || ... }"
|
|
|
|
case cType $ "CM"
|
|
cResult := '"' + uVal + '"'
|
|
|
|
case cType == "L"
|
|
cResult := iif( uVal, ".T.", ".F." )
|
|
|
|
case cType == "D"
|
|
cResult := DToC( uVal )
|
|
|
|
case cType == "N"
|
|
cResult := AllTrim( Str( uVal ) )
|
|
|
|
case cType == "O"
|
|
cResult := "Class " + uVal:ClassName() + " object"
|
|
endcase
|
|
|
|
return cResult
|
|
|
|
METHOD LineNumbers( lLineNumbers ) CLASS TDebugger
|
|
|
|
If( lLineNumbers == NIL, lLineNumbers := !::lLineNumbers, )
|
|
::lLineNumbers := lLineNumbers
|
|
::oPulldown:GetItemByIdent( "LINE" ):checked := ::lLineNumbers
|
|
IF ::oBrwText != NIL
|
|
::oBrwText:lLineNumbers := lLineNumbers
|
|
::oBrwText:RefreshAll()
|
|
ENDIF
|
|
|
|
return Self
|
|
|
|
METHOD Locate( nMode ) CLASS TDebugger
|
|
|
|
local cValue, lFound
|
|
|
|
DEFAULT nMode TO 0
|
|
|
|
cValue := ::InputBox( "Search string", ::cSearchString )
|
|
|
|
if empty( cValue )
|
|
return nil
|
|
endif
|
|
|
|
::cSearchString := cValue
|
|
|
|
lFound := ::oBrwText:Search( ::cSearchString, ::lCaseSensitive, 0 )
|
|
|
|
// Save cursor position to be restored by ::oWndCode:bGotFocus
|
|
::oWndCode:cargo[ 1 ] := Row()
|
|
::oWndCode:cargo[ 2 ] := Col()
|
|
|
|
RETURN lFound
|
|
|
|
METHOD FindNext() CLASS TDebugger
|
|
|
|
local lFound
|
|
|
|
if Empty( ::cSearchString )
|
|
lFound := ::Locate( 1 )
|
|
else
|
|
lFound := ::oBrwText:Search( ::cSearchString, ::lCaseSensitive, 1 )
|
|
|
|
// Save cursor position to be restored by ::oWndCode:bGotFocus
|
|
::oWndCode:cargo[ 1 ] := Row()
|
|
::oWndCode:cargo[ 2 ] := Col()
|
|
endif
|
|
|
|
return lFound
|
|
|
|
METHOD FindPrevious() CLASS TDebugger
|
|
|
|
local lFound
|
|
|
|
if Empty( ::cSearchString )
|
|
lFound := ::Locate( 2 )
|
|
else
|
|
lFound := ::oBrwText:Search( ::cSearchString, ::lCaseSensitive, 2 )
|
|
|
|
// Save cursor position to be restored by ::oWndCode:bGotFocus
|
|
::oWndCode:cargo[ 1 ] := Row()
|
|
::oWndCode:cargo[ 2 ] := Col()
|
|
endif
|
|
|
|
return lFound
|
|
|
|
METHOD RemoveWindow( oWnd ) CLASS TDebugger
|
|
|
|
local n := AScan( ::aWindows, { | o | o == oWnd } )
|
|
|
|
if n != 0
|
|
::aWindows = ADel ( ::aWindows, n )
|
|
::aWindows = ASize( ::aWindows, Len( ::aWindows ) - 1 )
|
|
endif
|
|
|
|
::nCurrentWindow = 1
|
|
|
|
return nil
|
|
|
|
METHOD SearchLine() CLASS TDebugger
|
|
|
|
local cLine
|
|
|
|
cLine := ::InputBox( "Line number", "1" )
|
|
|
|
if Val( cLine ) > 0
|
|
::GotoLine ( Val( cLine ) )
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD LocatePrgPath( cPrgName ) CLASS TDebugger
|
|
|
|
local i
|
|
local iMax
|
|
local aPaths
|
|
local cRetPrgName
|
|
local cSep
|
|
|
|
if empty( ::aPathDirs )
|
|
::aPathDirs := PathToArray( ::cPathForFiles )
|
|
endif
|
|
|
|
cSep := HB_OsPathSeparator()
|
|
|
|
aPaths := ::aPathDirs
|
|
|
|
iMax := len( aPaths )
|
|
|
|
for i := 1 to iMax
|
|
cRetPrgName := aPaths[i] + cSep + cPrgName
|
|
if file( cRetPrgName )
|
|
exit
|
|
else
|
|
cRetPrgName := nil
|
|
endif
|
|
next i
|
|
|
|
return cRetPrgName
|
|
|
|
METHOD ToCursor() CLASS TDebugger
|
|
LOCAL cLine
|
|
|
|
cLine := ::oBrwText:GetLine( ::oBrwText:nRow )
|
|
IF( ::oBrwText:lLineNumbers )
|
|
cLine := SUBSTR( cLine, AT(":",cLine)+1 )
|
|
ENDIF
|
|
IF( IsValidStopLine( cLine ) )
|
|
::aToCursor := { ::oBrwText:nRow, strip_path( ::cPrgName ) }
|
|
::RestoreAppStatus()
|
|
::lToCursor := .t.
|
|
::Exit()
|
|
ENDIF
|
|
|
|
RETURN self
|
|
|
|
METHOD NextRoutine() CLASS TDebugger
|
|
|
|
::RestoreAppStatus()
|
|
::lNextRoutine := .t.
|
|
::Exit()
|
|
|
|
RETURN self
|
|
|
|
|
|
METHOD WatchpointAdd( cExpr ) CLASS TDebugger
|
|
LOCAL cErr
|
|
LOCAL aWatch
|
|
|
|
IF( cExpr == NIL )
|
|
cExpr:=SPACE(255)
|
|
cExpr := ALLTRIM( ::InputBox( "Enter Watchpoint", cExpr ) )
|
|
IF( LASTKEY() == K_ESC )
|
|
RETURN self
|
|
ENDIF
|
|
ENDIF
|
|
cExpr := ALLTRIM( cExpr )
|
|
IF( EMPTY(cExpr) )
|
|
RETURN self
|
|
ENDIF
|
|
aWatch := {"wp", cExpr, NIL}
|
|
cErr := CreateExpression( cExpr, aWatch )
|
|
IF( !EMPTY(cErr) )
|
|
ALERT( cErr )
|
|
RETURN self
|
|
ENDIF
|
|
AADD( ::aWatch, aWatch )
|
|
::WatchpointsShow()
|
|
|
|
RETURN self
|
|
|
|
METHOD TracepointAdd( cExpr ) CLASS TDebugger
|
|
LOCAL cErr
|
|
LOCAL aWatch
|
|
LOCAL lSuccess
|
|
LOCAL uValue
|
|
|
|
IF( cExpr == NIL )
|
|
cExpr:=SPACE(255)
|
|
cExpr := ALLTRIM( ::InputBox( "Enter Tracepoint", cExpr ) )
|
|
IF( LASTKEY() == K_ESC )
|
|
RETURN self
|
|
ENDIF
|
|
ENDIF
|
|
cExpr := ALLTRIM( cExpr )
|
|
IF( EMPTY(cExpr) )
|
|
RETURN self
|
|
ENDIF
|
|
aWatch := {"tp", cExpr, NIL}
|
|
cErr := CreateExpression( cExpr, aWatch )
|
|
IF( !EMPTY(cErr) )
|
|
ALERT( cErr )
|
|
RETURN self
|
|
ENDIF
|
|
AADD( ::aWatch, aWatch )
|
|
uValue := GetWatchValue( aWatch, @lSuccess )
|
|
AADD( ::aTrace, { LEN(::aWatch), IIF(lSuccess, uValue, NIL )} )
|
|
::lTracepoints :=.T.
|
|
::WatchpointsShow()
|
|
|
|
RETURN self
|
|
|
|
|
|
METHOD WatchPointsShow() CLASS TDebugger
|
|
|
|
local nWidth, n := 1
|
|
Local oCol
|
|
local lRepaint := .f.
|
|
local nTop
|
|
LOCAL lFocused
|
|
|
|
if ::lGo
|
|
return nil
|
|
endif
|
|
|
|
if LEN(::aWatch) == 0
|
|
return nil
|
|
endif
|
|
|
|
if ::oWndPnt == nil
|
|
nTop := IIF(::oWndVars!=NIL .AND. ::oWndVars:lVisible,::oWndVars:nBottom,0) + 1
|
|
::oWndPnt := TDbWindow():New( nTop,;
|
|
0, ;
|
|
nTop +Min( 4, Len( ::aWatch ) ) + 1,;
|
|
MaxCol() - iif( ::oWndStack != nil, ::oWndStack:nWidth(), 0 ),;
|
|
"Watch" )
|
|
|
|
// ::oBrwText:Resize( ::oWndPnt:nBottom + 1 )
|
|
// ::oWndCode:nTop := ::oWndPnt:nBottom + 1
|
|
// ::oBrwText:Resize( ::oWndCode:nTop + 1 )
|
|
// ::oBrwText:RefreshAll()
|
|
// ::oWndCode:SetFocus( .t. )
|
|
|
|
// ::oWndPnt:bLButtonDown := { | nMRow, nMCol | ::WndVarsLButtonDown( nMRow, nMCol ) }
|
|
// ::oWndPnt:bLDblClick := { | nMRow, nMCol | ::EditVar( ::oBrwPnt:Cargo[ 1 ] ) }
|
|
|
|
::oBrwPnt := TDbgBrowser():New( nTop+1, 1, ::oWndPnt:nBottom - 1, MaxCol() - iif( ::oWndStack != nil,;
|
|
::oWndStack:nWidth(), 0 ) - 1 )
|
|
|
|
::oWndPnt:Browser := ::oBrwPnt
|
|
|
|
::oBrwPnt:Cargo :={ 1,{}} // Actual highligthed row
|
|
::oBrwPnt:ColorSpec := ::aColors[ 2 ] + "," + ::aColors[ 5 ] + "," + ::aColors[ 3 ]
|
|
::oBrwPnt:GOTOPBLOCK := { || ::oBrwPnt:cargo[ 1 ] := Min( 1, Len(::aWatch) ) }
|
|
::oBrwPnt:GoBottomBlock := { || ::oBrwPnt:cargo[ 1 ] := Len( ::aWatch ) }
|
|
::oBrwPnt:SkipBlock = { | nSkip, nOld | nOld := ::oBrwPnt:Cargo[ 1 ],;
|
|
::oBrwPnt:Cargo[ 1 ] += nSkip,;
|
|
::oBrwPnt:Cargo[ 1 ] := Min( Max( ::oBrwPnt:Cargo[ 1 ], 1 ),;
|
|
Len( ::aWatch ) ),;
|
|
IIF( LEN(::aWatch) > 0, ::oBrwPnt:Cargo[ 1 ] - nOld, 0 ) }
|
|
|
|
nWidth := ::oWndPnt:nWidth() - 1
|
|
oCol:=TBColumnNew( "", ;
|
|
{ || PadR( IIF( LEN( ::aWatch ) > 0, ;
|
|
AllTrim( Str( ::oBrwPnt:Cargo[1] -1 ) ) + ") " + ;
|
|
::WatchGetInfo( ::aWatch[ Max( ::oBrwPnt:Cargo[1], 1 ) ] ), ;
|
|
" " ), ;
|
|
::oWndPnt:nWidth() - 2 ) } )
|
|
::oBrwPnt:AddColumn( oCol )
|
|
AAdd(::oBrwPnt:Cargo[2], ::aWatch)
|
|
oCol:DefColor:={1,2}
|
|
|
|
::oWndPnt:bPainted := { || if(Len(::aWatch) > 0, ( ::oBrwPnt:refreshAll():ForceStable(),RefreshVarsS(::oBrwPnt) ),) }
|
|
|
|
::oWndPnt:bKeyPressed := { | nKey | ( iif( nKey == K_DOWN ;
|
|
, ::oBrwPnt:Down(), nil ), iif( nKey == K_UP, ::oBrwPnt:Up(), nil ) ;
|
|
, iif( nKey == K_PGDN, ::oBrwPnt:PageDown(), nil ) ;
|
|
, iif( nKey == K_PGUP, ::oBrwPnt:PageUp(), nil ) ;
|
|
, iif( nKey == K_HOME, ::oBrwPnt:GoTop(), nil ) ;
|
|
, iif( nKey == K_END, ::oBrwPnt:GoBottom(), nil ) ;
|
|
, iif( nKey == K_DEL, ::WatchpointDel( ::oBrwPnt:Cargo[1] ), nil ) ;
|
|
, iif( nKey == K_ENTER, ::WatchpointEdit( ::oBrwPnt:Cargo[1] ), nil ), ::oBrwPnt:ForceStable() ) }
|
|
|
|
AAdd( ::aWindows, ::oWndPnt )
|
|
::oWndPnt:Show()
|
|
::ResizeWindows( ::oWndPnt )
|
|
else
|
|
if( ::oBrwPnt:cargo[1] <= 0 )
|
|
::oBrwPnt:cargo[1] := 1
|
|
endif
|
|
DispBegin()
|
|
if Len( ::aWatch ) > ::oWndPnt:nBottom - ::oWndPnt:nTop - 1
|
|
//Resize( top, left, bottom, right)
|
|
::oWndPnt:Resize( ,, ::oWndPnt:nTop + Min( Len( ::aWatch ) + 1, 4 ) )
|
|
lRepaint :=.T.
|
|
elseif Len( ::aWatch ) < ::oWndPnt:nBottom - ::oWndPnt:nTop - 1
|
|
::oWndPnt:Resize( ,, ::oWndPnt:nTop + Len( ::aWatch ) + 1 )
|
|
lRepaint :=.T.
|
|
else
|
|
::oBrwPnt:refreshAll():forceStable()
|
|
endif
|
|
if ! ::oWndPnt:lVisible .OR. lRepaint
|
|
::ResizeWindows( ::oWndPnt )
|
|
endif
|
|
DispEnd()
|
|
endif
|
|
|
|
return nil
|
|
|
|
METHOD WatchpointEdit( nPos ) CLASS TDebugger
|
|
LOCAL cExpr
|
|
LOCAL aWatch
|
|
LOCAL cErr
|
|
|
|
cExpr:=PADR( ::aWatch[nPos][WP_EXPR], 255 )
|
|
cExpr := ALLTRIM( ::InputBox( "Enter Watchpoint", cExpr ) )
|
|
IF( LASTKEY() == K_ESC )
|
|
RETURN self
|
|
ENDIF
|
|
cExpr := ALLTRIM( cExpr )
|
|
IF( EMPTY(cExpr) )
|
|
RETURN self
|
|
ENDIF
|
|
aWatch := {"wp", cExpr, NIL}
|
|
cErr := CreateExpression( cExpr, aWatch )
|
|
IF( !EMPTY(cErr) )
|
|
ALERT( cErr )
|
|
RETURN self
|
|
ENDIF
|
|
::aWatch[ nPos ] := aWatch
|
|
::WatchpointsShow()
|
|
|
|
RETURN self
|
|
|
|
|
|
METHOD WatchpointDel( nPos ) CLASS TDebugger
|
|
LOCAL nIdx
|
|
|
|
IF( ::oWndPnt != NIL .AND. ::oWndPnt:lVisible )
|
|
IF( nPos == NIL )
|
|
//called from the menu
|
|
nPos := ::InputBox( "Enter item number to delete", ::oBrwPnt:cargo[1]-1 )
|
|
ELSE
|
|
nPos--
|
|
ENDIF
|
|
IF( LastKey() != K_ESC )
|
|
IF( nPos >=0 .AND. nPos < LEN(::aWatch) )
|
|
::oBrwPnt:gotop()
|
|
IF( ::aWatch[nPos+1][WP_TYPE] == "tp" )
|
|
nIdx := ASCAN( ::aTrace, {|a| a[TR_IDX]==nPos+1} )
|
|
IF( nIdx > 0 )
|
|
ADEL( ::aTrace[nIdx] )
|
|
ASIZE( ::aTrace, LEN(::aTrace)-1 )
|
|
::lTracepoints := LEN(::aTrace) > 0
|
|
ENDIF
|
|
ENDIF
|
|
ADEL( ::aWatch, nPos+1 )
|
|
ASIZE( ::aWatch, LEN(::aWatch)-1 )
|
|
IF( LEN(::aWatch) == 0 )
|
|
::WatchpointsHide()
|
|
ELSE
|
|
::WatchpointsShow()
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN self
|
|
|
|
|
|
METHOD WatchpointsHide() CLASS TDebugger
|
|
|
|
::oWndPnt:Hide()
|
|
::oWndCode:nTop := IIF(::oWndVars!=NIL .AND. ::oWndVars:lVisible, ::oWndVars:nBottom+1,1)
|
|
::oBrwText:Resize( ::oWndCode:nTop+1 )
|
|
if ::aWindows[ ::nCurrentWindow ] == ::oWndPnt
|
|
::NextWindow()
|
|
ENDIF
|
|
|
|
return nil
|
|
|
|
|
|
STATIC FUNCTION GetWatchValue( aWatch, plSuccess )
|
|
LOCAL aVars, i, j
|
|
LOCAL nLen, aLocVars
|
|
LOCAL cVar, nPos, cName
|
|
LOCAL xVal
|
|
LOCAL oErr
|
|
LOCAL bEBlock
|
|
|
|
plSuccess := .F.
|
|
bEblock := ErrorBlock( {|o| BREAK(o)} )
|
|
BEGIN SEQUENCE
|
|
IF( aWatch[WP_BLOCK] != NIL )
|
|
nLen :=LEN(aWatch)-WP_BLOCK
|
|
IF( nLen > 0 )
|
|
aVars := ARRAY( nLen )
|
|
FOR i:=1 TO nLen
|
|
cVar := aWatch[ i + WP_BLOCK ]
|
|
//search local variables in current procedure
|
|
aLocVars := s_oDebugger:aProcStack[1][CSTACK_LOCALS]
|
|
nPos := ASCAN( aLocVars, {|a| a[VAR_NAME]==cVar} )
|
|
IF( nPos > 0 )
|
|
j :=hb_dbg_ProcLevel() - aLocVars[ nPos ][ VAR_LEVEL ]
|
|
aVars[i] := hb_dbg_vmVarLGet( j, aLocVars[ nPos ][ VAR_POS ] )
|
|
ELSE
|
|
//search local statics
|
|
aLocVars := s_oDebugger:aProcStack[1][CSTACK_STATICS]
|
|
nPos := ASCAN( aLocVars, {|a| a[VAR_NAME]==cVar} )
|
|
IF( nPos > 0 )
|
|
aVars[i] := hb_dbg_vmVarSGet( aLocVars[ nPos ][VAR_LEVEL], aLocVars[ nPos ][VAR_POS] )
|
|
ELSE
|
|
//search global statics
|
|
cName := s_oDebugger:aProcStack[1][CSTACK_MODULE]
|
|
nPos := ASCAN( __dbgStatics, {|a| a[1]==cName} )
|
|
IF( nPos > 0 )
|
|
aLocVars := __dbgStatics[nPos][ 2 ]
|
|
nPos := ASCAN( aLocVars, {|a| a[VAR_NAME]==cVar} )
|
|
IF( nPos > 0 )
|
|
aVars[i] :=hb_dbg_vmVarSGet( aLocVars[ nPos ][VAR_LEVEL], aLocVars[ nPos ][VAR_POS] )
|
|
EXIT
|
|
ENDIF
|
|
ENDIF
|
|
IF( nPos == 0 )
|
|
aVars[i] := &cVar
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
NEXT
|
|
ENDIF
|
|
|
|
xVal := EVAL( aWatch[WP_BLOCK], aVars )
|
|
plSuccess :=.T.
|
|
ENDIF
|
|
|
|
RECOVER USING oErr
|
|
xVal := oErr:description
|
|
END SEQUENCE
|
|
ErrorBlock( bEBlock )
|
|
|
|
RETURN xVal
|
|
|
|
METHOD WatchGetInfo( aWatch ) CLASS TDebugger
|
|
LOCAL xVal
|
|
LOCAL ctype
|
|
LOCAL lValid
|
|
|
|
xVal := GetWatchValue( aWatch, @lValid )
|
|
IF( lValid )
|
|
cType := VALTYPE( xVal )
|
|
xVal := ValToStr( xVal )
|
|
ELSE
|
|
//xVal contains error description
|
|
cType := 'U'
|
|
// xVal := "Undefined"
|
|
ENDIF
|
|
|
|
RETURN aWatch[WP_EXPR]+" <"+aWatch[WP_TYPE]+", " +cType+">: " +xVal
|
|
|
|
METHOD ResizeWindows( oWindow ) CLASS TDebugger
|
|
LOCAL oWindow2, nTop, i
|
|
|
|
IF( oWindow == ::oWndVars )
|
|
oWindow2 := ::oWndPnt
|
|
ELSEIF( oWindow == ::oWndPnt )
|
|
oWindow2 := ::oWndVars
|
|
ENDIF
|
|
|
|
DispBegin()
|
|
IF( oWindow2 == NIL )
|
|
nTop := oWindow:nBottom +1
|
|
ELSE
|
|
IF( oWindow2:lVisible )
|
|
IF( oWindow:nTop < oWindow2:nTop )
|
|
nTop := oWindow2:nBottom - oWindow2:nTop + 1
|
|
oWindow2:Resize( oWindow:nBottom+1,, oWindow:nBottom+nTop)
|
|
ELSE
|
|
nTop := oWindow:nBottom - oWindow:nTop + 1
|
|
oWindow:Resize( oWindow2:nBottom+1,, oWindow2:nBottom+nTop)
|
|
ENDIF
|
|
nTop := MAX( oWindow:nBottom, oWindow2:nBottom ) + 1
|
|
ELSE
|
|
IF( oWindow:nTop > 1 )
|
|
nTop := oWindow:nBottom - oWindow:nTop + 1
|
|
oWindow:Resize( 1, , nTop )
|
|
ENDIF
|
|
nTop := oWindow:nBottom + 1
|
|
ENDIF
|
|
ENDIF
|
|
|
|
oWindow:hide()
|
|
IF( oWindow2 != NIL )
|
|
oWindow2:hide()
|
|
ENDIF
|
|
::oWndCode:Resize( nTop )
|
|
IF( oWindow2 != NIL )
|
|
oWindow2:show()
|
|
ENDIF
|
|
oWindow:show()
|
|
DispEnd()
|
|
|
|
RETURN self
|
|
|
|
|
|
METHOD Step() CLASS TDebugger
|
|
// we are starting to run again so reset to the deepest call if
|
|
// displaying stack
|
|
if ! ::oBrwStack == nil
|
|
::oBrwStack:GoTop()
|
|
endif
|
|
::RestoreAppStatus()
|
|
::Exit()
|
|
RETURN nil
|
|
|
|
|
|
|
|
STATIC FUNCTION CreateExpression( cExpr, aWatch )
|
|
LOCAL nLen
|
|
LOCAL i,j
|
|
LOCAL c, lSpace
|
|
LOCAL cID, cBV, nPos
|
|
LOCAL oErr, oEBlock
|
|
LOCAL cRet
|
|
|
|
nLen := LEN(cExpr)
|
|
i := j := 1
|
|
lSpace :=.T.
|
|
DO WHILE( i <= nLen )
|
|
c := SUBSTR( cExpr, i, 1 )
|
|
IF( c == '_' .OR. ISALPHA(c) )
|
|
i++
|
|
DO WHILE( i<=nLen .AND. IsIdentChar(c:=SUBSTR(cExpr,i,1)) )
|
|
i++
|
|
ENDDO
|
|
cID := UPPER( SUBSTR( cExpr, j, i-j ) )
|
|
IF( i < nLen )
|
|
DO WHILE( SUBSTR(cExpr,i,1)==" ")
|
|
i++
|
|
ENDDO
|
|
IF( SUBSTR(cExpr,i,1) == '(' )
|
|
//function call
|
|
j := i+1
|
|
LOOP
|
|
ENDIF
|
|
IF( SUBSTR(cExpr,i,2) == "->" )
|
|
//alias expressions are not expanded
|
|
i += 2
|
|
DO WHILE( i<=nLen .AND. IsIdentChar(SUBSTR(cExpr,i,1)," ()") )
|
|
i++
|
|
ENDDO
|
|
j := i+1
|
|
LOOP
|
|
ENDIF
|
|
ENDIF
|
|
nPos := ASCAN( aWatch, {|c| c==cID}, WP_BLOCK+1 )
|
|
IF( nPos == 0 )
|
|
AADD( aWatch, cID )
|
|
nPos := LEN( aWatch )
|
|
ENDIF
|
|
|
|
cBV := "__dbg[" +NTRIM(nPos - WP_BLOCK) +"]"
|
|
cExpr := LEFT( cExpr, j-1 ) + cBV + SUBSTR( cExpr, i )
|
|
nLen := LEN(cExpr)
|
|
i := j + LEN(cBV)
|
|
lSpace := .F.
|
|
|
|
ELSEIF( c $ " +-*/^!=<>(" )
|
|
lSpace := .T.
|
|
i++
|
|
|
|
ELSEIF( c == '&' ) //skip macro expression
|
|
i++
|
|
DO WHILE( i<=nLen .AND. IsIdentChar(SUBSTR(cExpr,i,1)," ()") )
|
|
i++
|
|
ENDDO
|
|
|
|
ELSEIF( c == '.' ) //skip logical values
|
|
i++
|
|
IF( SUBSTR(cExpr,i,1) $ "TtFf" .AND. SUBSTR(cExpr,i+1,1) == '.' )
|
|
i +=2
|
|
ENDIF
|
|
|
|
ELSEIF( c == ':' ) //skip send operator
|
|
i++
|
|
DO WHILE( i<=nLen .AND. IsIdentChar(SUBSTR(cExpr,i,1)) )
|
|
i++
|
|
ENDDO
|
|
|
|
ELSEIF( c == "'" .OR. c == '"' ) //STRING
|
|
i++
|
|
DO WHILE( i<=nLen .AND. SUBSTR(cExpr,i,1)!=c )
|
|
i++
|
|
ENDDO
|
|
i++
|
|
|
|
ELSEIF( c == "[" )
|
|
IF( lSpace )
|
|
//STRING
|
|
i++
|
|
DO WHILE( i<=nLen .AND. SUBSTR(cExpr,i,1)!="]" )
|
|
i++
|
|
ENDDO
|
|
ELSE
|
|
//array index
|
|
lSpace := .T.
|
|
i++
|
|
ENDIF
|
|
ELSE
|
|
i++
|
|
ENDIF
|
|
j := i
|
|
ENDDO
|
|
|
|
// s_oDebugger:InputBox("AFTER", cExpr )
|
|
oEBlock := ErrorBlock( {|o| BREAK(o)} )
|
|
BEGIN SEQUENCE
|
|
aWatch[WP_BLOCK] := &( "{|__dbg|"+ cExpr +"}" )
|
|
cRet := NIL
|
|
RECOVER USING oErr
|
|
cRet := "Expression error: " +oErr:description
|
|
aWatch[WP_BLOCK] := NIL
|
|
END SEQUENCE
|
|
ErrorBlock( oEBlock )
|
|
|
|
RETURN cRet
|
|
|
|
STATIC FUNCTION IsIdentChar( cChar, cSeeAlso )
|
|
|
|
IF( ISALPHA(cChar) .OR. ISDIGIT(cChar) .OR. cChar == '_' )
|
|
RETURN .T.
|
|
ENDIF
|
|
|
|
RETURN IIF(cSeeAlso!=NIL, cChar $ cSeeAlso, .F. )
|
|
|
|
STATIC PROCEDURE StripUntil( pcLine, i, cChar )
|
|
LOCAL j, n
|
|
LOCAL nLen:=LEN(pcLine)
|
|
|
|
n := LEN(cChar)
|
|
j := i+n
|
|
DO WHILE j<=nLen .AND. SUBSTR(pcLine, j, n) != cChar
|
|
j++
|
|
ENDDO
|
|
IF j <= nLen
|
|
pcLine := LEFT( pcLine, i-1 ) + SUBSTR(pcLine, j+n)
|
|
ENDIF
|
|
|
|
RETURN
|
|
|
|
STATIC FUNCTION IsValidStopLine( cLine )
|
|
LOCAL i, c, c2
|
|
|
|
cLine := UPPER( ALLTRIM( cLine ) )
|
|
i := 1
|
|
DO WHILE i <= LEN(cLine)
|
|
c := SUBSTR( cLine, i, 1 )
|
|
c2 := SUBSTR( cLine, i, 2 )
|
|
DO CASE
|
|
CASE c == '"'
|
|
StripUntil( @cLine, i, c )
|
|
|
|
CASE c == "'"
|
|
StripUntil( @cLine, i, c )
|
|
|
|
CASE c == "["
|
|
StripUntil( @cLine, i, "]" )
|
|
|
|
CASE c2 == "//"
|
|
cLine := LEFT( cLine, i-1 )
|
|
|
|
CASE c2 == "/*"
|
|
StripUntil( @cLine, i, "*/" )
|
|
|
|
OTHERWISE
|
|
i++
|
|
ENDCASE
|
|
ENDDO
|
|
|
|
cLine := ALLTRIM( cLine )
|
|
IF EMPTY(cLine)
|
|
RETURN .F.
|
|
ENDIF
|
|
|
|
c := Left( cLine, 4 )
|
|
IF ( Left( c, 3 ) == 'END' .OR.;
|
|
c == 'FUNC' .OR.;
|
|
c == 'PROC' .OR.;
|
|
c == 'NEXT' .OR.;
|
|
c == 'ELSE' .OR.;
|
|
c == 'LOCA' .OR.;
|
|
c == 'STAT' .OR.;
|
|
c == 'MEMV' )
|
|
|
|
RETURN .F.
|
|
ENDIF
|
|
|
|
RETURN .T.
|
|
|
|
|
|
function __DbgColors()
|
|
|
|
return iif( ! s_oDebugger:lMonoDisplay, s_oDebugger:aColors,;
|
|
{ "W+/N", "W+/N", "N/W", "N/W", "N/W", "N/W", "W+/N",;
|
|
"N/W", "W+/W", "W/N", "W+/N" } )
|
|
|
|
function __Dbg()
|
|
|
|
return s_oDebugger
|
|
|
|
static function myColors( oBrowse, aColColors )
|
|
local i
|
|
local nColPos := oBrowse:colpos
|
|
|
|
for i := 1 to len( aColColors )
|
|
oBrowse:colpos := aColColors[i]
|
|
oBrowse:hilite()
|
|
next
|
|
|
|
oBrowse:colpos := nColPos
|
|
|
|
return nil
|
|
|
|
static procedure RefreshVarsS( oBrowse )
|
|
|
|
local nLen := Len(oBrowse:aColumns)
|
|
|
|
if ( nLen == 2 )
|
|
oBrowse:dehilite():colpos:=2
|
|
endif
|
|
oBrowse:dehilite():forcestable()
|
|
if ( nLen == 2 )
|
|
oBrowse:hilite():colpos:=1
|
|
endif
|
|
oBrowse:hilite()
|
|
|
|
return
|
|
|
|
static function ArrayBrowseSkip( nPos, oBrwSets, n )
|
|
|
|
return iif( oBrwSets:cargo[ 1 ] + nPos < 1, 0 - oBrwSets:cargo[ 1 ] + 1 , ;
|
|
iif( oBrwSets:cargo[ 1 ] + nPos > Len(oBrwSets:cargo[ 2 ][ 1 ]), ;
|
|
Len(oBrwSets:cargo[ 2 ][ 1 ]) - oBrwSets:cargo[ 1 ], nPos ) )
|
|
|
|
/*
|
|
?? <expr>
|
|
displays inspect window with value or display nothing on error
|
|
? <expr>
|
|
displays either result or error description in command window
|
|
*/
|
|
static function DoCommand( o,cCommand )
|
|
LOCAL aCmnd
|
|
LOCAL cResult
|
|
LOCAL lValid
|
|
|
|
cCommand := ALLTRIM( cCommand )
|
|
aCmnd := { NIL, NIL, NIL }
|
|
IF( LEFT(cCommand,2) == "??" )
|
|
cCommand := SUBSTR( cCommand, 3 )
|
|
aCmnd[WP_TYPE] := "??"
|
|
|
|
ELSEIF( LEFT(cCommand,1) == "?" )
|
|
cCommand := SUBSTR( cCommand, 2 )
|
|
aCmnd[WP_TYPE] := "?"
|
|
ENDIF
|
|
aCmnd[WP_EXPR] := cCommand
|
|
cResult := CreateExpression( cCommand, aCmnd )
|
|
IF( EMPTY(cResult) )
|
|
//valid syntax
|
|
cResult := GetWatchValue( aCmnd, @lValid )
|
|
IF( aCmnd[WP_TYPE] == "??" )
|
|
IF( lValid )
|
|
o:Inspect( aCmnd[WP_EXPR], cResult )
|
|
ENDIF
|
|
cResult := '' //discard result
|
|
ELSE
|
|
IF( lValid )
|
|
cResult := ValToStr( cResult )
|
|
ENDIF
|
|
ENDIF
|
|
ELSE
|
|
IF( aCmnd[WP_TYPE] == "??" )
|
|
cResult := '' //ignore error
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN cResult
|
|
|
|
|
|
static function PathToArray( cList )
|
|
|
|
local nPos
|
|
local aList := {}
|
|
local cSep
|
|
local cDirSep
|
|
|
|
cSep := HB_OsPathListSeparator()
|
|
cDirSep := HB_OsPathDelimiters()
|
|
|
|
if ( cList <> NIL )
|
|
|
|
do while ( nPos := at( cSep, cList ) ) <> 0
|
|
aadd( aList, substr( cList, 1, nPos - 1 ) ) // Add a new element
|
|
cList := substr( cList, nPos + 1 )
|
|
enddo
|
|
|
|
aadd( aList, cList ) // Add final element
|
|
|
|
/* Strip ending delimiters */
|
|
AEval(aList, {|x, i| if( Right( x, 1 ) $ cDirSep, aList[ i ] := Left( x, Len( x ) - 1 ), ) } )
|
|
endif
|
|
|
|
return aList
|
|
|
|
|
|
/* Strip path from filename */
|
|
STATIC FUNCTION strip_path( cFileName )
|
|
LOCAL cName := "", cExt := ""
|
|
DEFAULT cFileName TO ""
|
|
|
|
HB_FNAMESPLIT( cFileName, NIL, @cName, @cExt )
|
|
RETURN cName + cExt
|
|
|
|
|