I haven't had time to code in DBC for about 2 years now and I realize I'm starting to forget a lot of the language and those things or methods I liked most: dealing with "limitations" or approaching interesting or tricky challenges.
I started going through my files today and came across a ton of code snippets and programs I had written. So many have no remarks and do something, but I can't remember what I was trying to test or prove. It kind of made me sad.
I did however, come across a program I wrote that would trace variables in a DBC program and output their values to a console screen in real time and also create a log file of their changing values. Fortunately, I had completely documented this one.
It's meant to be a library that can be added to any program. The main posting has an example in the middle of capturing specific values for a rotating cube. To use the library in other programs, just rem out or delete the following example code before the start of the library functions:
sync on
sync rate 60
dim tracefile(3)
rem initialize the trace program - first thing and only once
trace_init
rem make a cube
make object cube 1,25
_main:
tim=timer()+5000
mem=1
trace_file_open()
repeat
label$="repeat loop in main"
yrotate object 1,wrapvalue(object angle y(1)+2.7)
fsize=trace(mem,label$,i1,i1n$,i2,i2n$,object angle y(1),"Object 1 Y angle",f2#,f2n$,s1$,s1n$,s2$,s2n$)
rem if the file size is > 10000 bytes, stop recording and then start again
if fsize >= 10000
trace_file_close()
trace_file_open()
hnd=tracefile(2)
buf=get memblock ptr(mem)
kernel32=tracefile(0)
out$="Size at "+str$(fsize)+". Creating new trace file"
osize=len(out$)+1
result=call dll(kernel32,"WriteConsoleA",hnd,out$,osize,buf,0)
wait 2000
endif
sync
`until timer() > tim
until mouseclick()=2
trace_file_close()
end
Here's the complete code. Hopefully someone finds it useful. If you run it with the example code, RIGHT CLICK the mouse to stop the program and the trace. You may have to drag the console screen out of the way to view the cube. Keep in mid, you don't have to use trace_open() if you do not want to save a trace file. The output is always sent to the console.
remstart
==============================================================
= Title : Simple Variable Trace
= Author : Latch
= Date : 07/29/2007
= Update : 04/14/2009 , 07/30/2007
= Version: .03
==============================================================
Comments 04/14/2009
A simple trace routine. Allows the recording of six
variables at a time into a file for later review.
Send output to a console and also provide the option to
create a trace file that will store the variable information.
To apply a trace to a program, at the very top of the program
call
trace_init().
trace_init() initializes the trace routines. It creates a
console for output and also populates the array tracefile()
with the dll number used and the Handle to the console
output window
trace_file_open() creates and opens a file for recording the
traced variable output. This is useful for later monitoring
of the values of any variables.
trace_file_close() stops the recording of the trace information
to a file and closes the current filenumber associated with the
trace file.
Files are recorded in the current directory: dbc_trace.<count>
dbc_trace.001, dbc_trace.002 etc . A new file is created
every time trace_file_close() is called and trace_file_open()
is called again. You could start and stop a series of
trace file recordings any time you are running a trace on
program variables. The time and date is recorded in the trace
file and to reiterate, each new file is created with an
incremental extention (.001, .002, .003 etc.) - so you could
have a series of trace files in your directory for review.
The actual trace is performed by
trace(mem,label$,i1,i1n$,i2,i2n$,f1#,f1n$,f2#,f2n$,s1$,s1n$,s2$,s2n$)
The call is set up to allow the tracing of 6 different variables:
2 integers
2 floats
2 strings
mem == the memblock to use as a buffer
label$ == an identifying point in the program where this
particular trace is being called from. This
can be any text the users want to identify where
the trace is being run. You may set it to
the line number that the trace is called at
or you may indicate the sub routine in which
you are running the trace or even identify
a loop. Again, it can be any text you want that
helps to tell where in the program the trace
is being called from.
The next series of values, represent the variable to be
traced by type, and the variables name.
The function parameter types for the variables are identified
by the first letter:
i == integer
f == floating point (real number/ number with decimal)
s == string
Eace variable is numbered and paired with a partner string
that is used to hold the name of the varible.
i1 == the first integer variable you want to trace
i1n$ == the name of the first integer variable
i2 == the second integer variable you want to trace
i2n$ == the name of the second integer variable
f1# == the first float variable you want to trace
f1n$ == the name of the first float variable
f2# == the second float variable you want to trace
f2n$ == the name of the second float variable
s1$ == the first string variable you want to trace
s1n$ == the name of the first string variable
s2$ == the second string variable you want to trace
s2n$ == the name of the second string variable
6 variables may seem limiting, but you could call the trace
in succession multiple times with different sets of variables.
Also, you don't have to trace all 6 variables. You can trace
any number or combination of variables allowed in the function
parameters.
If you don't want to use a particular variable type, simply
make sure it's name value is set to "".
ex: I only want to check for two integer variables
named score and bullet and I want to indicate that the
trace is starting from line 10 - and I'm using memblock
1 as the buffer.
trace(1,"Line 10",score,"score",bullet,"bullet",f1#,"",f2#,"",s1$,"",s2$,"")
DBC Trace Program Variables
need to dim tracefile(3)
tracefile(0) stores the dll number for the kernel32 dll
tracefile(1) stores the file number for the output file
tracefile(2) stores the handle to the console
tracefile(3) stores the current filesize of the trace file
NOTE: A trace file can get VERY large if left to record
trace information indefinately as a program runs (especially
in a loop). The function trace() will return the
approximate file size in BYTES of the current trace file so
the user can decide what to do if the file is a
certain size. For example, one might check to
see if the trace file size is > 500000 bytes. If so
then call trace_file_close() and then call
trace_file_open() to continue recording in a new file
==============================================================
remend
sync on
sync rate 60
dim tracefile(3)
rem initialize the trace program - first thing and only once
trace_init
rem make a cube
make object cube 1,25
_main:
tim=timer()+5000
mem=1
trace_file_open()
repeat
label$="repeat loop in main"
yrotate object 1,wrapvalue(object angle y(1)+2.7)
fsize=trace(mem,label$,i1,i1n$,i2,i2n$,object angle y(1),"Object 1 Y angle",f2#,f2n$,s1$,s1n$,s2$,s2n$)
rem if the file size is > 10000 bytes, stop recording and then start again
if fsize >= 10000
trace_file_close()
trace_file_open()
hnd=tracefile(2)
buf=get memblock ptr(mem)
kernel32=tracefile(0)
out$="Size at "+str$(fsize)+". Creating new trace file"
osize=len(out$)+1
result=call dll(kernel32,"WriteConsoleA",hnd,out$,osize,buf,0)
wait 2000
endif
sync
`until timer() > tim
until mouseclick()=2
trace_file_close()
end
function trace_init()
rem go to window mode so we can see the console
rem retrieve a dll for kernel 32
set window on
if tracefile(1)=0 then kernel32=0
if kernel32=0
inc kernel32
while dll exist(kernel32)
inc kernel32
endwhile
load dll "kernel32.dll",kernel32
endif
rem create a console and get a handle to the standard output
result=call dll(kernel32,"AllocConsole")
if result=0
error=call dll(kernel32,"GetLastError")
inhnd=0
break "Error creating console : "+str$(error)
end
else
rem get a handle to the console output
STD_ERROR_HANDLE = -12
STD_INPUT_HANDLE = -10
STD_OUTPUT_HANDLE = -11
inhnd=call dll(kernel32,"GetStdHandle",STD_OUTPUT_HANDLE)
endif
tracefile(1)=kernel32
tracefile(2)=inhnd
endfunction
`----------------------------------------------------------------
function trace_file_open()
rem search for an available filenum
if filenum <= 0
inc filenum
while file open(filenum)
inc filenum
endwhile
endif
curdir$=get dir$()+"\"
ext#=.001
ext$=right$(str$(ext#),4)
if file open(filenum)=0
filename$=curdir$+"dbc_trace"
While file exist(filename$+ext$)=1
inc ext#,.001
ext$=right$(str$(ext#),4)
endwhile
open to write filenum,filename$+ext$
write string filenum,"Trace Started: "+get date$()+" "+get time$()
write string filenum,""
endif
tracefile(0)=filenum
endfunction
`----------------------------------------------------------------
function trace_file_close()
rem close the trace file being written to disk
filenum=tracefile(0)
if filenum > 0
if file open(filenum)=1
close file filenum
endif
endif
rem just being careful with cleanup
tracefile(0)=0
tracefile(3)=0
endfunction
`----------------------------------------------------------------
`function trace(mem,label$,v1,v2,v1$,v2$,v3#,v4#,v3$,v4$,v5$,v6$,v5n$,v6n$)
function trace(mem,label$,i1,i1n$,i2,i2n$,f1#,f1n$,f2#,f2n$,s1$,s1n$,s2$,s2n$)
rem return the file size of the trace file so the user can decide
rem what to do if it gets too big
rem create the buffer
if memblock exist(mem)=0
make memblock mem,4
buf=get memblock ptr(mem)
else
ms=get memblock size(mem)
if ms ! 4
break "Trace Memblock "+str$(mem)+" error: May already be in use."
end
endif
endif
dim variable$(6,2)
variable$(1,1)=str$(i1)
variable$(1,2)=i1n$
variable$(2,1)=str$(i2)
variable$(2,2)=i2n$
variable$(3,1)=str$(f1#)
variable$(3,2)=f1n$
variable$(4,1)=str$(f2#)
variable$(4,2)=f2n$
variable$(5,1)=s1$
variable$(5,2)=s1n$
variable$(6,1)=s2$
variable$(6,2)=s2n$
rem send to console
kernel32=tracefile(1)
hnd=tracefile(2)
for v=1 to 6
if variable$(v,2) <> ""
out$=variable$(v,2)+" = "+variable$(v,1)+chr$(10)
osize=len(out$)+1
result=call dll(kernel32,"WriteConsoleA",hnd,out$,osize,buf,0)
if result=0
error=call dll(kernel32,"GetLastError")
inhnd=0
break "Error writing to console : "+str$(error)
end
endif
endif
next v
rem if trace_file_open was called, tracefile(0) should have a file number
rem keep a general size of the file based on the string size
if tracefile(0) > 0
filenum=tracefile(0)
string1$="Trace looking at position: "+label$
rem length of the string + carriage return + linefeed
s1size=len(string1$)+2
write string filenum,string1$
for n=1 to 6
if variable$(n,2)<>""
string2$=variable$(n,2)+" = "+variable$(n,1)
s2size=len(string2$)+2
write string filenum,string2$
rem record file size
tracefile(3)=tracefile(3)+s2size
endif
next n
write string filenum,""
rem record file size
tracefile(3)=tracefile(3)+s1size
endif
filesize=tracefile(3)
rem cleanup
undim variable$(6,2)
endfunction filesize
Enjoy your day.