This is a work in progress. It's getting better, doesn't abend. I still have to make sure that the FIELDS= and CHANGE= reports are correct. Eventually I'll include source code, and maybe a better description. The source code (so far) is in:
https://sites.google.com/site/linlyons/saytrace-code
The background of SAYTRACE 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 the trace line was displayed. 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 (with SAYINIT). Then, you can check your progress, at many different points in the program by calling the trace routine (using 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 can 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 major 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.) I found this trace particularly helpful with the pentominoes program, and, needing a break, thought that if I could make it callable, then other folks might find it useful. How would you use it?
You need to assemble the program, and put the 2 macros (SAYINIT and SAY) into your source library. The SAY macro that creates the minimum amount of code, and generates a simple call to the trace routine. SAYINIT creates the control block used to define everything that you want SAYTRACE to do.
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)
DC CL4'END '
BALR 14,15
MEND
Hmmm, all of that just to generate 4 instructions.
The second is much more complicated, and generates the control block to keep all the options that the user specified.
MACRO
&LBL SAYINIT &STOP=999999999999999,®S=,&FIELDS=,&CHANGE=, X
&MAXSAY=100,&CSECT=0,&DCB=0
* SAYINIT STOP=0,DCB=0,REGS=(4,5,6),MAXSAY=100,CSECT=NAME, X X
* FIELDS=(SAV,8,SET4,4),CHANGE=(&SYSNDX,1,0,INIT,400,0),
LCLC &L,&A,&B
LCLA &N,&M
&N SETA &SYSNDX
&L SETC 'SYS&N'
.* &N SETA 1
.* &M SETA 2
.* &A SETC '&FIELDS(&N)' <=== WORKS
.* &B SETC '&FIELDS(1+&N)' <== WORKS
.* MNOTE ,'A= &A '
.* MNOTE ,' B=&B '
CNOP 0,4
&LBL BAL 1,&L.Z
DC A(*+4)
DC CL6'SAYINI'
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
DC X'FF'
.NOREGS 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 user program, looks like:
DSECT DSECT 0
DS CL6 IDENTIFIER
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
DCB DS A DCB ADDRESS USER DEFINED AND OPENED DCB
CSECTADR DS A CSECT ADDRESS USER PROGRAM LOAD ADDRESS
MAXSAY DS F MAX SAY, SO WE CAN GETMAIN ENOUGH SPACE
FIELDS DS A A(FIELDS) LIST POINTER TO (LOC,LEN,LOC,LEN)
ACHANGE DS A A(CHANGES) LIST POINTER TO (LOC,LEN,0,LOC,LEN,0)
LCHANGE EQU 12
There is a getmained area that contains data save areas for each change address, eg
CHANGE=(LABEL,LENGTH,0,LABEL,LENGTH,0) eg, 2 compare fields, 3 words each.
Also in the getmained area, there is a 10 byte call address/count field for each location we're called from.
This is an extract from the report. The NEW/OLD lines are from change= compares. A report line starts with LABEL (or program offset). The 2 numbers are times called. I use R9 for BAL, or sometimes r14, so those registers show me where I came from. (I generally use high regs for base and dsect address, and lower ones for work, with 14,15,0,1 being used as often as possible.) After the registers are the FIELDS= memory contents.
GETMAIN = 0002000 BYTES
003 CHANGE= AREAS SETUP
TESTLOOP 01 01 R9=F4F4F4F4 RE=800FFA72 0122 D507 A008A010 4780C3EE 58DD
TESTBRK 02 01 R9=F4F4F4F4 RE=800FFA82 0122 D507 A008A010 4780C3EE 58DD
NEW 02B7 89
OLD 0FF2B7 7D
NEW 02D4 E3
OLD 0FF2D4 00
0A92 03 01 R9=F4F4F4F4 RE=800FFA92 0122 D507 A008A010 4780C3EE 58DD
NEW 02B7 95
OLD 0FF2B7 89
NEW 02D4 E3
OLD 0FF2D4 00
TESTLOOP 04 02 R9=F4F4F4F4 RE=800FFA72 0122 D507 A008A010 4780C3EE 58DD
TESTBRK 05 02 R9=F4F4F4F4 RE=800FFA82 0122 D507 A008A010 4780C3EE 58DD
0A92 06 02 R9=F4F4F4F4 RE=800FFA92 0122 D507 A008A010 4780C3EE 58DD
TESTLOOP 07 03 R9=F4F4F4F4 RE=800FFA72 0122 D507 A008A010 4780C3EE 58DD
TESTBRK 08 03 R9=F4F4F4F4 RE=800FFA82 0122 D507 A008A010 4780C3EE 58DD
0A92 09 03 R9=F4F4F4F4 RE=800FFA92 0122 D507 A008A010 4780C3EE 58DD
TESTLOOP 010 004 R9=F4F4F4F4 RE=800FFA72 0122 D507 A008A010 4780C3EE 58DD
TESTBRK 011 004 R9=F4F4F4F4 RE=800FFA82 0122 D507 A008A010 4780C3EE 58DD
0A92 012 004 R9=F4F4F4F4 RE=800FFA92 0122 D507 A008A010 4780C3EE 58DD
TESTLOOP 013 005 R9=F4F4F4F4 RE=800FFA72 0122 D507 A008A010 4780C3EE 58DD
There is a self-test routine built into SAYTRACE, that I used for testing.
However, I also wrote a program that I used for testing, and also serves as a sample.
SAYTEST START 0
YREGS
USING *,13
B BEGIN-*(R15)
DC 17F'0',C'SAYTEST &SYSDATE &SYSTIME'
STM 14,12,12(13)
BEGIN ST 13,4(15)
ST 15,8(13)
LR 13,15
SAYINIT REGS=(9,14),FIELDS=(OPEN,24,GET,12), Z
CHANGE=(GET,12,0,PUT,12,0,#LINE,3,00)
PUSH PRINT
PRINT NOGEN
OPEN OPEN (IN,INPUT,OUT,OUTPUT)
POP PRINT
SAY 'AFTROPEN'
LOOP SAY ,
PUT OUT,IDMSG
BAL R9,GET
BAL R9,TRANSLAT
BAL R9,PUT
B LOOP
*
PUT SAY
PUT OUT,LINE
PUT OUT,LINE2
PUT OUT,LINE3
BR R9
*
GET GET IN
SAY ,
AP #LINE,P1
MVC LINE(120),0(R1)
BR R9
*
TRANSLAT SAY ,
MVC LINE2,LINE
MVC LINE3,LINE
SAY 'TR'
TR LINE2,TOP
TR LINE3,BOTTOM
BR R9
*
TOP DC 16C'0',16C'1',16C'2',16C'3',16C'4',16C'5',16C'6',16C'7'
DC 16C'8',16C'9',16C'A',16C'B',16C'C',16C'D',16C'E',16C'F'
BOTTOM DC 16C'0123456789ABCDEF'
*
#LINE DC PL4'0'
Z CLOSE (IN,,OUT)
SAY 'Z-DONE'
L 13,4(13)
LM 14,12,12(13)
SR 15,15
BR 14
LTORG
P1 DC X'1C'
LINE DC CL133' '
LINE2 DC CL133' '
LINE3 DC CL133' '
IDMSG DC CL133' SAYTEST, ASM &SYSDATE AT &SYSTIME'
PUSH PRINT
PRINT NOGEN
IN DCB DDNAME=IN,DSORG=PS,LRECL=1330,RECFM=FT,MACRF=GL,EODAD=Z
OUT DCB DDNAME=OUT,DSORG=PS,LRECL=133,RECFM=FT,MACRF=PM
POP PRINT
*
@@PAD#0 EQU *-SAYTEST+4095 FOR TESTING, I LIKE MODULES THAT ARE
@@PAD#1 EQU @@PAD#0/(4097) ON 4K BOUNDARIES. WHAT THAT DOES IS
@@PAD#2 EQU (@@PAD#1*4096) MAKE LOCATIONS AND OFFSETS MATCH.
ORG SAYTEST+@@PAD#2 HD SECTORS ARE LARGER THAN 4K SO
THERE'S NO SPACE LOST.
END SAYTEST