home *** CD-ROM | disk | FTP | other *** search
- {
- ErrTrace - a unit for TurboPascal v4.0 to display an error traceback
- information.
-
- Author : Michal Jankowski <sieminski@rzsin.sin.ch>
- <sieminski%rzsin.sin.ch@cernvax> (Bitnet)
- Version : 1.0 16.09.1988
- }
-
- {$N- No floating point needed }
- {$B- Boolean short circuit }
- {$R- No range checking needed }
- {$D- No debug information }
-
- unit ErrTrace;
- {-----------------------------------------------------------------------------}
- interface
- const
- Continue : boolean = false;
-
- {-----------------------------------------------------------------------------}
- implementation
-
- {
- convert integer to hex
- }
- type
- hexWord = string[4];
-
- function hex( x : word ) : hexWord;
- const hexDigit : array [0..15] of char = '0123456789ABCDEF';
- begin
- hex := hexDigit[(x shr 12) and $f ] +
- hexDigit[(x shr 8) and $f ] +
- hexDigit[(x shr 4) and $f ] +
- hexDigit[ x and $f ];
- end; { function hex }
-
- const
- FarCallOpcode : byte = $9A;
- NearCallOpcode : byte = $E8;
- PushbpOpcode : byte = $55;
- MovbpspOpcode : word = $E589;
-
- var
- NF : char; { 'N' for near, 'F' for far calls }
- Errorcs,
- Errorip,
- newip,
- adr,
- newcs,
- _cs,
- _ip,
- _bp : word;
- first,
- found : boolean;
- ExitSave : pointer;
-
- type
- ErrorMsg = record
- ErrTxt : String[32]; { Longest message has 32 characters }
- ErrNo : integer;
- end;
-
- const
- ErrorMsgSize = 26; { Number of messages }
- ErrorMsgs : array[1..ErrorMsgSize] of ErrorMsg = (
- { Run-time error messages }
- ( ErrTxt: 'Division by zero'; ErrNo: 200 ),
- ( ErrTxt: 'Range check error'; ErrNo: 201),
- ( ErrTxt: 'Stack overflow error'; ErrNo: 202),
- ( ErrTxt: 'Heap overflow error'; ErrNo: 203),
- ( ErrTxt: 'Invalid pointer operation'; ErrNo: 204),
- ( ErrTxt: 'Floating point overflow'; ErrNo: 205),
- ( ErrTxt: 'Floating point underflow'; ErrNo: 206),
- ( ErrTxt: 'Invalid floating point operation'; ErrNo: 207),
- { I/O error messages }
- ( ErrTxt: 'File not found'; ErrNo: 2),
- ( ErrTxt: 'Path not found'; ErrNo: 3),
- ( ErrTxt: 'Too many open files'; ErrNo: 4),
- ( ErrTxt: 'File access denied'; ErrNo: 5),
- ( ErrTxt: 'Invalid file handle'; ErrNo: 6),
- ( ErrTxt: 'Invalid file access code'; ErrNo: 12),
- ( ErrTxt: 'Invalid drive number'; ErrNo: 15),
- ( ErrTxt: 'Cannot remove current directory'; ErrNo: 16),
- ( ErrTxt: 'Cannot rename across drives'; ErrNo: 17),
- ( ErrTxt: 'Disk read error'; ErrNo: 100),
- ( ErrTxt: 'File not open'; ErrNo: 103),
- ( ErrTxt: 'File not open for input'; ErrNo: 104),
- ( ErrTxt: 'File not open for output'; ErrNo: 105),
- ( ErrTxt: 'Invalid numeric format'; ErrNo: 106),
- ( ErrTxt: 'Disk write error'; ErrNo: 101),
- ( ErrTxt: 'File not assigned'; ErrNo: 102),
- ( ErrTxt: 'Drive not ready'; ErrNo: 152),
- ( ErrTxt: 'Unknown Error'; ErrNo: 0));
-
- {$f+}
- procedure ErrorTrap;
- {$f-}
-
- var
- i : integer; { Index to table of messages, also used to }
- { the stack, must be the FIRST local variable }
- begin
- if (ExitCode<>0) { only on error exits }
- and (ExitCode<>255) then begin { not on user break }
- { Look for error number in table }
- i := 0;
- repeat
- i := i+1;
- until
- (ErrorMsgs[i].ErrNo = ExitCode) { found }
- or (i = ErrorMsgSize); { use 'Unknown error' message }
-
- { Now look for traceback information }
- { i is the first local variable, use it to find local stack }
- adr := ofs(i)+2; { Get offset of bottom of our stack }
- _bp := memw[sseg:adr]; { Get old bp from stack }
- found := false;
- Errorcs := Seg(ErrorAddr^)+PrefixSeg+$10;
- { Convert relative segment to absolute }
- Errorip := Ofs(ErrorAddr^);
- _cs := Errorcs;
- _ip := Errorip;
- { Look for far call to error-check routine - 'normal' errors }
- if (mem[_cs:_ip-5]= FarCallOpcode) { Found far call }
- { It should be : far call to error-check routine, }
- { then from it far call to our procedure }
- { Compare segments }
- and (memw[_cs:_ip-2] = memw[sseg:adr+4])
- { Offsets differ by less than $80 - assume that was a call from }
- { error-check routine }
- and (abs(integer(memw[_cs:_ip-4]-memw[sseg:adr+2]))<$80) then begin
- found := true;
- end;
- { Not found, so it must be arithmetic (80x87) error }
- if not found then begin
- { First look for errors in initialization part of unit }
- { Units have special entry sequence - no 'push bp' instruction }
- _ip := memw[sseg:_bp]; { Get return address from stack }
- _cs := memw[sseg:_bp+2];
- { First look for far call to erroneous routine }
- if (mem[_cs:_ip-5]=FarCallOpCode) then begin
- newip := memw[_cs:_ip-4];
- _cs := memw[_cs:_ip-2];
- { Look for special entry sequence }
- if (memw[_cs:newip]=MovbpspOpcode) then begin
- found := true;
- end;
- end;
- end;
- if not found then begin
- { Now look for errors in procedure reached by far call }
- _ip := memw[sseg:_bp+2]; { Get return address from stack (skip old bp) }
- _cs := memw[sseg:_bp+4];
- { First look for far call to erroneous routine }
- if (mem[_cs:_ip-5]=FarCallOpcode) then begin
- newip := memw[_cs:_ip-4];
- _cs := memw[_cs:_ip-2];
- { Look for standard entry sequence }
- if (mem[_cs:newip]=PushbpOpcode) and (memw[_cs:newip+1]=MovbpspOpcode) then begin
- found := true;
- end;
- end;
- end;
- if not found then
- { Now look for errors in procedure reached by near call }
- { This is tricky, because we don't know cs at the time of error - }
- { ErrorAdr gives only 'normalized pointer'. But it was pushed on stack }
- { somewhere by the actual 80x87 interrupt, so... }
- repeat { look for old cs on stack }
- _cs := memw[sseg:adr]; { try next word from stack for cs }
- { ip is already taken from stack }
- if mem[_cs:_ip-3]=NearCallOpcode then begin
- newip := _ip+memw[_cs:_ip-2]; { Near calls are relative }
- { Look for standard entry sequence }
- if (mem[_cs:newip]=PushbpOpcode) and (memw[_cs:newip+1]=MovbpspOpcode) then begin
- found := true;
- end;
- end;
- inc(adr,2); { point to next word on stack }
- until found or (adr>_bp);{ stop when stack ends }
-
- if not found then begin
- { Nothing found on stack, so assume main program }
- _cs := PrefixSeg+$10;
- found := true; { Always true! }
- end;
-
- if found then begin
- { For 8087 errors, ErrorAdr is a 'normalized' pointer, so convert it }
- inc(Errorip,$10*(Errorcs-_cs));
- Errorcs := _cs;
- end;
- { Write message, use relative segment }
- writeln('Runtime error ',ExitCode,' at ',hex(Errorcs-PrefixSeg-$10),':',
- hex(Errorip));
- writeln(ErrorMsgs[i].ErrTxt);
- if found then begin
- first := true;
- { Now loop thru traceback ... }
- repeat
- found := false;
- _ip := memw[sseg:_bp+2]-3; { point to assumed 'call' instruction }
- { try near call }
- if mem[_cs:_ip]=NearCallOpcode then begin
- newip := _ip+3+memw[_cs:_ip+1];
- { Look for standard entry sequence }
- if (mem[_cs:newip]=PushbpOpcode) and (memw[_cs:newip+1]=MovbpspOpcode) then begin
- newcs := _cs;
- found := true;
- NF := 'N';
- end;
- end;
- if not found then begin
- _ip := _ip-2; { Adjust for far call }
- newcs := memw[sseg:_bp+4]; { Get cs }
- if mem[newcs:_ip]=FarCallOpcode then begin
- { It should be call to cs at previous level, so check it }
- if (memw[newcs:_ip+3] = _cs) then begin
- _cs := newcs;
- newip := memw[_cs:_ip+1];
- newcs := memw[_cs:_ip+3];
- { Look for standard entry sequence }
- if (mem[newcs:newip]=PushbpOpcode) and (memw[newcs:newip+1]=MovbpspOpcode) then begin
- found := true;
- NF := 'F';
- end;
- end;
- end;
- end;
- if found then begin
- if first then begin
- { Here on first pass, but only if there is anything to print }
- writeln('Traceback');
- first := false;
- end;
- { Write message, use relative segments again }
- writeln(NF,' Procedure at ',hex(newcs-PrefixSeg-$10),':',hex(newip),
- ' Called from ',hex(_cs-PrefixSeg-$10),':',hex(_ip));
- _bp := memw[sseg:_bp];
- end;
- until not found;
- end;
- if not Continue then
- halt(ExitCode); { Halt program }
- end; { if ExitCode<>0 }
- { On normal exit, or if Continue = true, proceed to next ExitProc in chain }
- ExitProc := ExitSave;
- end; { ErrorTrap }
-
- begin
- ExitSave := ExitProc; { Save old pointer }
- ExitProc := @ErrorTrap; { Install our procedure }
- end.