This is a work in progress. Not done yet. Abends unexpectedly. Eventually I'll include source code, and maybe a better description.
The objective is, I was working on another program (pentominoes puzzle solutions), and needed a different kind of trace, one that triggers after #### of times that the trace routine is called.
SAYTRACE is that different kind of trace. Normally, you set a break point and stop when you get there. But what if you only want to stop on the 30,000th time through? Have I got a deal for you. SAYTRACE allows you to do that. How does it work you might ask?
First you do an initial run, and see that your program messed up after the ####th time you posted a trace line. So what you want to do is code STOP=#### so you can stop at that point, or maybe 1 or 2 entries before it, to let you can see what's going on.
How does it work? First you need to set up the control block. Then, you can check your progress, at many different points in the program by calling the trace routine (use the SAY macro) from everywhere that seems interesting. The trace routine reports the total number of times trace was called, and also the number of times from each specific location. The report is generally via WTO, but could be written to a file. A couple of sample print lines via WTO will contain, the LABEL that it's called from (or program location) 2 count fields, (total # calls, and # calls from this location), optionally, up to 7 registers (2 and 3 in this sample) and selected memory locations you want to see. For example:
TESTLOOP 07 03 R2=000FFA3C R3=F4F4F4F4 0092 D507A000
TESTBRK 08 03 R2=000FFA3C R3=F4F4F4F4 0092 D507A000
- The label shows where we came from,
- There are 2 count fields that are, total number of trace calls, and number from this location,
- Optionally, register contents can be displayed,
- After registers some memory that might be useful can be displayed.
I'm also working on a routine (not working well yet) that shows user specified sections of memory that have changed. EG:
NEW 02FF 9D <=== 2 byte program offset and new data
OLD 0FF2FF 91 <=== 3 byte actual memory location and old data
NEW 031C C4
OLD 0FF31C 00
TESTBRK 02 01 R2=000FFA3C R3=F4F4F4F4 R4=F4F4F4F4 0092 D507A000
If your program went south after the 3,814th time that trace was called, you can specify STOP=3813 to stop and let you see why the problem occurred. The program is written to be used from an assembler program. I suppose it could be used from COBOL, but it would need some changes.
So what's going on? Why am I doing this? At 84, some mental exercise is probably a good idea. There are a couple other programs I was working on, one to solve pentominoes puzzle for 4 different board sizes. Another to scan a file for strings, with various select options. (I did my first one 50+ years ago, but I kind of like the idea.)
The question now is, if I finish this, would anyone use it? Would anyone find it useful? There are a couple MACROs to make it easy to use. The first is the SAY macro that creates the minimum amount of code, and generates a simple call to the trace routine.
MACRO
&LBL SAY &TEXT
LCLC "E
"E SETC ''''
&LBL L R15,=V(SAYTRACE)
SR R1,R1
AIF ('&TEXT'(1,1) EQ '"E').DC
AIF ('&TEXT' EQ 'END').END
AIF ('&TEXT' EQ '').NOTEXT
MNOTE 12,'SAY MACRO ERROR'
.NOTEXT AIF ('&LBL' EQ '').NOLBL
BALR 14,15
DC CL8'&LBL'
` MEXIT
.NOLBL BALR 14,15
DC AL2(0)
MEXIT
.DC BALR 14,R15
DC CL8&TEXT
MEXIT
.END BAL 1,*+12
DC AL4(*+4),C'END '
BALR 14,15
MEND
The second is much more complicated, and generates the control block to keep all the data that's used.
MACRO
&LBL SAYINIT &STOP=999999999999999,®S=,&FIELDS=,&CHANGE=, X
&MAXSAY=100,&CSECT=0,&DCB=0
* SAYINIT STOP=123,DCB=0,REGS=(4,5,6),MAXSAY=100,CSECT=NAME, X X
* FIELDS=(SAV,8,SET4,4),CHANGE=(&SYSNDX,32,0,INIT,400,0),
LCLC &L
LCLA &N,&M
&N SETA &SYSNDX
&L SETC 'SYS&N'
CNOP 2,4
BAL 1,&L.Z
DC AL4(*+4)
DC AL2(&L.Z-&L.A-5)
&L.A DC PL8'0' SAY#
DC PL8'&STOP' STOP=###
&L.R DC 8X'FF'
AIF ('®S' EQ '').NOREGS
ORG *-8
DC AL1®S MACRO EXPANSION ERROR
ORG &L.R+7
.NOREGS DC X'FF'
DC A(&DCB) DCB
DC A(&CSECT) CSECT ADDRESS
DC A(&MAXSAY) MAX SAY INSTS USED
.*
&L.$ DC A(&L.F,&L.C) FIELDS= AND CHANGE=
AIF ('&FIELDS' EQ '').NOF
&L.F DC A&FIELDS,X'FFFFFFFF'
AGO .TRYC
.NOF ORG &L.$
DC X'FF'
ORG
&L.F DC X'FFFFFFFF'
.*
.TRYC AIF ('&CHANGE' EQ '').NOC
&L.C DC AL4&CHANGE,X'FFFFFFFF'
AGO .PASTC
.NOC ORG L&$+4
DC X'FF'
ORG
&L.C DC X'FFFFFFFF'
.PASTC DC V(SAYTRACE)
&L.Z L R15,*-4
BALR 14,15
MEND
For change=(field,length,0), there are 3 entries for each field. Field location, length, and 0 where I'll put the getmained address to save data with which to compare. I didn't want to have users code the "0" but it sure makes things easier, and I don't need a 2nd location to keep the entry. The control block, which is kept in the use program, looks like:
DSECT DSECT 0
DS HL2 LENGTH OF PARAM LIST
SAY# DS PL8 SAY COUNT # of calls
STOP DS PL8 STOP AT SAY # stop after this many
REGS DS XL8 REGS LIST registers to show
DCB DS A DCB ADDRESS DCB address
CSECTADR DS A CSECT ADDRESS user program load address.
MAXSAY DS F max # calls. we build the list dynamically.
FIELDS DS A A(FIELDS) pointer to the list of location/length pairs to list
ACHANGE DS A A(CHANGES) pointer to 3 words that contain location, length, w/a
- and then there are,
CHANGE=(LABEL,LENGTH,0,LABEL,LENGTH,0) 2 compare fields, 3 words each,
10 byte call address/count fields for each location we're called from.