$!/* acf3:comp.os.vms / leichter@LRW.COM (Jerry Leichter) / 1:22 pm Apr 6, 1991 */ $! $! I am using a set of malloc and free replacements to aid in finding $! memory leaks. They work like this: $! $! #define malloc(n) test_malloc(n, __FILE__, __LINE__) $! $! and in the malloc_test.c file: $! $! void * test_malloc(int n, char *filename, int linenumber) $! { $! printf("Malloced %d bytes at line %d of file %s\n", n, $! linenumber, filename); $! return malloc(n); $! } $! $! Oh well, to the point. What I would like to do is to get rid of the $! __LINE__ and __FILE__ macros, and somehow access the traceback $! information. DEBUG has this information, because it is stored in the $! EXE, but: how can I access this? $! $!There is no documented way to do this, and - curiously, considering how often $!the question has come up over the years - I haven't seen anyone cobble $!together any unsupported code to do it either. I HAVE seen unsupported code $!(by Nick de Smith) to call the system traceback routine; I've enclosed a copy $!I saved of his message below. $! $!Note that de Smith's elegant hack isn't really necessary: You can get exactly $!the same effect by signaling some dummy condition with a severity of Warning. $!The default traceback handler will print a traceback and then continue execu- $!tion. (If the program is run /DEBUG, however, the Debugger will stop it.) $! $! As this is probably asking for the impossible (or highly undocumented, $! which is roughly equivalent), I'll settle for the nonsymbolic stack $! info DEBUG gives you with the "SHOW STACK 3" command. The latter was $! implemented in the december, 1990 issue of VAX professional on $! profiling. Unfortunately, the article doesn't include a listing of $! the "tiny MACRO subroutine" which does the dirty work. $! $!Actually, this is easy to do. By signalling SS$_DEBUG, your program can get $!the Debugger running. It can also pass one or more commands for the Debugger $!to execute. This is all documented in the Debugger book. $! $! Simply having the call stack info ("I was called from PC 0012f348, who $! in turn was called from 00058db0") will give enough information to $! quickly locate the offending code in a linker map. $! $!This is very easy to do if you understand the VAX architecture. You'd write $!a little function that would examine its own stack frame to see where it was $!called from, then walk backward, stack frame by stack frame (they are threaded $!through by the saved FP fields) printing the saved PC values. The function $!could be written in MACRO; or a couple of lines of MACRO could return the $!starting FP to a C program, with everything else in C; or you could even $!write the whole thing in C using the built_in _READ_GPR instruction to read $!the FP (register 13). $! -- Jerry $! $!----------------- $!Date: Tue, 7 Aug 90 9:40 BST $!From: Nick de Smith $!To: INFO-VAX <@NSFnet-Relay.AC.UK:INFO-VAX@crvax.sri.com> $!Subject: RE: Calling TRACEBACK $! $!Hi, $! $!I have had many requests for the interface to TRACEBACK, so I am posting a SHORT $!routine that can be called from any language to create a traceback listing and $!then continue. It can easily be integrated into a AST delivery program to be $!dropped onto another process. This is left as an exercise for the reader - If $!you get really stuck I'll send the specific code for that to interested parties. $! $!TRACE.1_OF_1 follows at the end of this message. $! $!Just for the record, I have to pay for ALL incoming AND outgoing messages from $!my site. As we are a commercial organisation, we can afford the $100/pcm for the $!mail and the $12000/year for the data line. However, if I want to release a $!largish package (like PDUMP a few weeks ago) I still feel that its MY $!responsibility to distribute it to interested parties, even though I have so far $!had to mail (via a VMSSERV) over 200 copies of this 46Kbyte package. If I were $!an academic or private user, I almost certainly could not afford to do this, and $!thus would be very reluctant to distribute it personally, and would thus have to $!post it, or not to distribute it at all. I cannot receive general newsgroups so $!the vms.sources route is not an option. I could ask for someone else to carry $!the cost of "serving" the package, but why should they? $! $!The point I am rambling on about is that there is NO simple answer to the $!post/don't post situation - there are so many different types of users on the $!network that NO general rule can be applied to all of them. The main tenet used $!by Info-VAX subscribers is RESPONSIBILITY. Just try to be aware of the $!consequences of your actions. If its any consellation, I have come back from a $!month away to find 100s of Kbytes of (largly) rubbish on this issue - far more $!than the cost of letting the occasional posting go through. $! $!Yours in nearly terminal boredom, $! $!nick NICK@NCDLAB.ULCC.AC.UK $! $! ------------------ CUT HERE ----------------------- $ v='f$verify(f$trnlnm("SHARE_VERIFY"))' $! $! This archive created by VMS_SHARE Version 7.2-007 22-FEB-1990 $! On 7-AUG-1990 09:17:18.47 By user NICK $! $! This VMS_SHARE Written by: $! Andy Harper, Kings College London UK $! $! Acknowledgements to: $! James Gray - Original VMS_SHARE $! Michael Bednarek - Original Concept and implementation $! $! TO UNPACK THIS SHARE FILE, CONCATENATE ALL PARTS IN ORDER $! AND EXECUTE AS A COMMAND PROCEDURE ( @name ) $! $! THE FOLLOWING FILE(S) WILL BE CREATED AFTER UNPACKING: $! 1. PROCESS_TRACE.MAR;1 $! $set="set" $set symbol/scope=(nolocal,noglobal) $f=f$parse("SHARE_TEMP","SYS$SCRATCH:.TMP_"+f$getjpi("","PID")) $e="write sys$error ""%UNPACK"", " $w="write sys$output ""%UNPACK"", " $ if f$trnlnm("SHARE_LOG") then $ w = "!" $ ve=f$getsyi("version") $ if ve-f$extract(0,1,ve) .ges. "4.4" then $ goto START $ e "-E-OLDVER, Must run at least VMS 4.4" $ v=f$verify(v) $ exit 44 $UNPACK: SUBROUTINE ! P1=filename, P2=checksum $ if f$search(P1) .eqs. "" then $ goto file_absent $ e "-W-EXISTS, File ''P1' exists. Skipped." $ delete 'f'* $ exit $file_absent: $ if f$parse(P1) .nes. "" then $ goto dirok $ dn=f$parse(P1,,,"DIRECTORY") $ w "-I-CREDIR, Creating directory ''dn'." $ create/dir 'dn' $ if $status then $ goto dirok $ e "-E-CREDIRFAIL, Unable to create ''dn'. File skipped." $ delete 'f'* $ exit $dirok: $ w "-I-PROCESS, Processing file ''P1'." $ if .not. f$verify() then $ define/user sys$output nl: $ EDIT/TPU/NOSEC/NODIS/COM=SYS$INPUT 'f'/OUT='P1' PROCEDURE Unpacker ON_ERROR ENDON_ERROR;SET(FACILITY_NAME,"UNPACK");SET( SUCCESS,OFF);SET(INFORMATIONAL,OFF);f:=GET_INFO(COMMAND_LINE,"file_name");b:= CREATE_BUFFER(f,f);p:=SPAN(" ")@r&LINE_END;POSITION(BEGINNING_OF(b)); LOOP EXITIF SEARCH(p,FORWARD)=0;POSITION(r);ERASE(r);ENDLOOP;POSITION( BEGINNING_OF(b));g:=0;LOOP EXITIF MARK(NONE)=END_OF(b);x:=ERASE_CHARACTER(1); IF g=0 THEN IF x="X" THEN MOVE_VERTICAL(1);ENDIF;IF x="V" THEN APPEND_LINE; MOVE_HORIZONTAL(-CURRENT_OFFSET);MOVE_VERTICAL(1);ENDIF;IF x="+" THEN g:=1; ERASE_LINE;ENDIF;ELSE IF x="-" THEN IF INDEX(CURRENT_LINE,"+-+-+-+-+-+-+-+")= 1 THEN g:=0;ENDIF;ENDIF;ERASE_LINE;ENDIF;ENDLOOP;t:="0123456789ABCDEF"; POSITION(BEGINNING_OF(b));LOOP r:=SEARCH("`",FORWARD);EXITIF r=0;POSITION(r); ERASE(r);x1:=INDEX(t,ERASE_CHARACTER(1))-1;x2:=INDEX(t,ERASE_CHARACTER(1))-1; COPY_TEXT(ASCII(16*x1+x2));ENDLOOP;WRITE_FILE(b,GET_INFO(COMMAND_LINE, "output_file"));ENDPROCEDURE;Unpacker;QUIT; $ delete/nolog 'f'* $ CHECKSUM 'P1' $ IF CHECKSUM$CHECKSUM .eqs. P2 THEN $ EXIT $ e "-E-CHKSMFAIL, Checksum of ''P1' failed." $ ENDSUBROUTINE $START: $ create 'f' X.title`09PROCESS_TRACE`09Generate a process traceback X;++ X; PROCESS_TRACE X; X; Date :`0902-Jan-89 X; Author:`09Nick de Smith X;`09`09Applied Telematics Services, 7 Vale Avenue X;`09`09Tunbridge Wells, Kent TN1 1DJ, England. X;`09`09+44 892 511000, PSI%234213300154::NICK X; X; Copyright (c) 1989 by Applied Telematics Group Limited and Nick de Smith. X; This software is supplied for information only. No guarantee is supplied f Vor X; this software, and no liability will be accepted for any action resulting V from X; the use of this software or the information contained herein. Under no X; circumstances may this software be used for commercial gain, including its V sale, X; lease or loan. This software may be copied only with the inclusion of this X; copyright notice. The author is prepared to enter into correspondance with X; interested parties, but will not necessarily maintain this software. Havin Vg said X; all that, enjoy it!`20 X; X; Description: X; X; This routine invokes the VMS traceback facility to generate an image trace Vback X; of the current process whenever called. The calling process may then conti Vnue. X; A fake "signal" of SS$_DEBUG is used for the condition, the PC and PSL bei Vng X; those active when PROCESS_TRACE was called. The traceback is displayed on V SYS$OUTPUT. X; X; Note that for some reason (best known to itself) TRACE executes a $EXIT, s Vo we X; trap it with an exit handler, and convert it into a RET. See the comments V in the X; code for EXIT_HANDLER, below. X; X; Note that TRACE may have side effects, eg. it uses EFN 0 for output. I thi Vnk that X; this routine is "safe", but, as with all undocumented features, don't walk V in X; with blind faith. X; X; Edit`09Edit date`09By`09Why X; 01`0902-Jan-89`09NMdS`09First attempt X;-- X`09.Ident`09/V01.01/ X X`09.library`09"SYS$LIBRARY:LIB" X`09.link`09`09"SYS$SYSTEM:SYS.STB" /Selective_Search X X`09$chfdef`09`09`09`09; Define condition handler values X`09$iacdef`09`09`09`09; Image activator definitions X`09$ihddef`09`09`09`09; Image file header definitions X`09$psldef`09`09`09`09; Process status definitions X`09$sfdef`09`09`09`09; Define saved frame offsets X`09$ssdef`09`09`09`09; Define system statii X`09$vadef`09`09`09`09; Define virtual address offsets X`0C X`09.subtitle`09TRACE data area X`09.psect`09$DATA`09Rd, Wrt, NoShr, NoExe, Pic, Long X; X; TRACE traceback vector. This is required so that TRACE can locate the X; stack frames and condition that it has to report on. The layout of this X; vector is defined in module SYSIMGSTA. X; X`09$defini`09TRACE X`09. = 0 X`09$def`09FAULT_PC`09.blkl`091`09; "Fault PC" X`09$def`09FAULT_FP`09.blkl`091`09; "Fault FP" X`09$def`09FIRST_FP`09.blkl`091`09; FP when user image invoked X`09$def`09SIGNAL_ARRAY`09.blkl`091`09; => Fake signal array X`09$def`09SECT_BOUNDS`09.blkq`091`09; Beginning of TRACE symbol table X`09$def`09TRACE_CTX_SIZ`09`09`09; Size of TRACE's own storage X`09$defend`09TRACE X XIMGNAM:`09.ascid "TRACE"`09`09`09; Name of the traceback utility to use XDFLNAM:`09.ascid "SYS$LIBRARY:.EXE"`09; Defaults to use for the name XHDRBUF:`09.blkb`09512`09`09`09; Buffer to use for image header XINADR:`09.long`09`5Ex200, 1@VA$V_P1-1`09; Map TRACE anywhere in P0 XRETADR:`09.long`090, 0`09`09`09; Returned mapped range X XVMS_EXIT_STATUS:`09`09`09; TRACE's $EXIT status X`09.long`090 XDESBLK:`09`09`09`09`09; $EXIT handler block X`09.long`090`09`09`09; FLINK for exit block X`09.address EXIT_HANDLER`09`09; Exit handler to use X`09.long`090`09`09`09; Argument count X`09.address VMS_EXIT_STATUS`09; Where to place $EXIT status XSAVED_FP: .long`090`09`09`09; Saved FP of PROCESS_TRACE X`0C X.subtitle PROCESS_TRACE`09Generate an image traceback X;++ X; PROCESS_TRACE - Generate an image traceback X; X; Call:`09`09status.wl = PROCESS_TRACE() X; X; We generate an argument list for TRACE: X; X;`094(ap)`09=> TRACE vector X;`098(ap)`09=> CLI parse information routine X;`0912(ap)`09=> IHD (image header) X;`0916(ap)`09=> IFD (image file descriptor) X;`0920(ap)`09= LINK status bits (IHD$L_LNKFLAGS) X;`0924(ap)`09= 0 (CLI status bits (eg. CLI$V_xxxx)) X; X; Passed: X;`09lots of implicit inputs (your whole process environment) X; X; Returns: X; `09R0`09= Final system status of call X;-- X`09.psect $CODE`09Rd, NoWrt, Shr, Exe, Pic, Long X X.entry`09PROCESS_TRACE, `5Em ; Must save al Vl registers X; X`09movab`09g`5ELIB$SIG_TO_RET, SF$A_HANDLER(fp) ; Return all errors to calle Vr X; X; Build TRACE's traceback vector on the stack X; X`09subl`09#TRACE_CTX_SIZ, sp`09; Get some space for TRACE own data X`09movl`09sp, r0`09`09`09; R0 => TRACE context area X`09movl`09SF$L_SAVE_PC(fp), FAULT_PC(r0) ; ...callers PC in traceback vector X`09movl`09SF$L_SAVE_FP(fp), FAULT_FP(r0)`09; ...callers FP in traceback vect Vor X`09clrq`09SECT_BOUNDS(r0)`09`09; Init section bounds for TRACE X; X; Locate frame of main program (first frame to TRACE from) X; X`09movl`09fp, r1`09`09`09; R1 => Our frame X10$:`09movl`09r1, r2`09`09`09; Save this frame X`09movl`09SF$L_SAVE_FP(r1), r1`09; R1 => Callers frame X`09tstl`09SF$L_SAVE_FP(r1)`09; Is there a previous frame? X`09bneq`0910$`09`09`09; Branch if there is X`09movl`09r2, FIRST_FP(r0)`09; Save first FP in traceback vector X; X; Build fake signal array on the stack X; X`09subl`09#CHF$S_CHFDEF1+<1*4>, sp ; Get space for signal array X`09movl`09sp, r1`09`09`09; R1 => Fake signal array X`09movl`09#3, CHF$L_SIG_ARGS(r1)`09; Three arguments... X`09movl`09#SS$_DEBUG, CHF$L_SIG_NAME(r1)`09; ...false signal is SS$_DEBUG X`09movl`09SF$L_SAVE_PC(fp), CHF$L_SIG_ARG1(r1)`09; ...caller's PC X`09movpsl`09CHF$L_SIG_ARG1+4(r1)`09; ...caller's PSL X`09movl`09r1, SIGNAL_ARRAY(r0)`09; Save pointer in fake argument list X; X; Build TRACE's argument list X; X`09subl`09#<7*4>, sp`09`09; Get some space for a fake argument list X`09movl`09sp, ap`09`09`09; AP => EXE$IMGSTA style fake argument list X`09movzbl`09#6, (ap)`09`09; 6 arguments X`09movl`09r0, 4(ap)`09`09; => TRACE vector X`09movab`09g`5EEXE$CLI_UTILSRV, 8(ap) ; => Dummy CLI callback (not used) X`09movq`09g`5EMMG$IMGHDRBUF, r0`09; R0 => IHD, R1 => IFD X`09movl`09r0, 12(ap)`09`09; => Image IHD X`09movl`09r1, 16(ap)`09`09; => Image file IFD X`09movl`09IHD$L_LNKFLAGS(r0), 20(ap) ; LINK status bits (not used) X`09clrl`0924(ap)`09`09`09; CLI status bits (not used) X; X; Activate the TRACE image X; X`09$imgact_s`09`09 -`09; X`09`09name = IMGNAM`09,-`09; Name of traceback utility X`09`09dflnam = DFLNAM`09,-`09; Name defaults to use X`09`09hdrbuf = HDRBUF`09,-`09; Where to place header X`09`09imgctl = #,- ; Merge and expand region X`09`09inadr = INADR`09,-`09; Where to map code (first free VA space) X`09`09retadr = RETADR`09,-`09; Mapped addresses returned here X`09`09ident = 0`09,-`09; No ident specified X`09`09acmode = #PSL$C_USER`09; Map at minimum access mode X`09blbc`09r0, 20$`09`09`09; Check for any error X`09$imgfix_s`09`09`09; Do any relocation fixups X`09blbc`09r0, 20$`09`09`09; Check for any error X X`09movl`09fp, SAVED_FP`09`09; Save our FP X`09$dclexh_s -`09`09`09; Trap $EXIT by TRACE X`09`09desblk = DESBLK X`09blbc`09r0, 20$`09`09`09; Check for error X X`09movl`09RETADR, r1`09`09; Get load address X`09movab`09@8(r1)`5Br1`5D, r1`09`09; Calculate the entry address X`09jsb`09(r1)`09`09`09; Call the traceback utility X`09; Never get here, as TRACE executes a $EXIT. See the comments X`09; for EXIT_HANDLER below for an explanation. X20$:`09ret`09`09`09`09; Return to caller X`0C X`09.subtitle`09EXIT_HANDLER`09Trap TRACE's $EXIT call X;+ X; EXIT_HANDLER`09Exit handler for PROCESS_TRACE X; X; The above routine, PROCESS_TRACE, executes a JSB to the TRACE module. X; TRACE terminates with a $EXIT. In order to allow the program calling X; this module to continue, this exit handler traps the $EXIT and searches X; back through the call frames for the frame of the establisher of the X; exit handler (which is, of course, PROCESS_TRACE). The RET from the X; exit handler is executed in the context of PROCESS_TRACE, which will X; return (after restoring all registers) to the caller of PROCESS_TRACE X; such that the $EXIT is dismissed, and so that the main program may X; continue to execute. Nasty, maybe, but it works! X;- XEXIT_HANDLER: X`09.word`09`5Em<>`09`09`09; No save mask X`09movl`09fp, r1`09`09`09; => Current FP X10$:`09movl`09SF$L_SAVE_FP(r1), r1`09; => Caller's FP X`09cmpl`09r1, SAVED_FP`09`09; Was it our caller's? X`09bneq`0910$`09`09`09; No X`09movl`09r1, fp`09`09`09; Yes, set it as current FP X`09movl`09VMS_EXIT_STATUS, r0`09; Make $EXIT status the return code X`09ret`09`09`09`09; Back to mainline code X X`09.end X`0C $ CALL UNPACK PROCESS_TRACE.MAR;1 1263716253 $ v=f$verify(v) $ EXIT /* ---------- */