Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2004-11

ansistrings, exceptions, pascal call stack and access violations

9 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

David Emerson

Hi all,

We've got a sophisticated system which relies heavily on the power of ansistrings. I'm now introducing exception handling into the system, and am running into problems with the pascal call stack.

Without exception handling, I get a nice pascal stack, such as the following (produced by a test program, source below):

Runtime error 200 at $004010A8
$004010A8 TEST_DIVISION_BY_ZERO, line 13 of e:/c/test/t.pas $00401179 PLAY_WITH_STRINGS, line 23 of e:/c/test/t.pas $004011B1 main, line 28 of e:/c/test/t.pas

However, if I include the sysutils unit, and catch (and reraise) all exceptions, I get the following -- which not only lacks the complete call stack, but includes lots of junk:

An unhandled exception occurred at 0x004010A8 :
EDivByZero : Division by zero
$004010A8 TEST_DIVISION_BY_ZERO, line 13 of e:/c/test/t.pas $666F2033
An unhandled exception occurred at 0x004088FB :
EAccessViolation : Access violation
$004088FB
$00406278
$005CFDA0
$666F2033
An unhandled exception occurred at 0x004088FB :
EAccessViolation : Access violation
$004088FB
$00406278
$005CFB30
$00406278
$005CFDA0
$666F2033
An unhandled exception occurred at 0x004088FB :
EAccessViolation : Access violation
$004088FB
$00406278
$005CF8C0
$00406278
$005CFB30
$00406278
$005CFDA0
$666F2033
An unhandled exception occurred at 0x004088FB :
EAccessViolation : Access violation
$004088FB
$00406278
$005CF650
$00406278
$005CF8C0
$00406278
$005CFB30
$00406278
$005CFDA0
$666F2033

I noticed in my test program that if I change the ansistring_param to a normal string I get the call stack back:

An unhandled exception occurred at 0x00401078 :
EDivByZero : Division by zero
$00401078 TEST_DIVISION_BY_ZERO, line 13 of e:/c/test/t.pas $004010F3 PLAY_WITH_STRINGS, line 23 of e:/c/test/t.pas $0040116B main, line 30 of e:/c/test/t.pas

(compared to the first: notice exception handling is now working, and the third line has changed from "main, line 28" to line 30 -- where raise is called, rather than where play_with_strings is called)

Here's the test source:

	{$mode objfpc}   // (line 1)
	uses sysutils;
	procedure do_nothing_with_an_ansistring (ansistring_param : string);
	   begin   end;
	procedure test_division_by_zero;
	   var
	      zero : integer;
	   begin
	      zero := 0;
	      write (5 div zero);
	   end;
	procedure play_with_strings;
	   var
	      short_string : string;
	   begin
	      short_string := 'HELLO';
	      if (lowercase(short_string)='hello')
	         then do_nothing_with_an_ansistring (short_string);
	      test_division_by_zero;
	   end;
	begin
	   try
	      play_with_strings;   // (line 28)
	   except
	      on exception do raise;   // (line 30)
	      end; // try
	end.

The only change I've made to fpc.cfg is uncommenting the -gl switch.

Perhaps there's another switch that I need to set in order to make ansistrings and exceptions work harmoniously?

Thanks,
David

Marco van de Voort
to David Emerson
Show quoted text 12 lines
We've got a sophisticated system which relies heavily on the power of ansistrings. I'm now introducing exception handling into the system, and am running into problems with the pascal call stack. Without exception handling, I get a nice pascal stack, such as the following (produced by a test program, source below): Runtime error 200 at $004010A8 $004010A8 TEST_DIVISION_BY_ZERO, line 13 of e:/c/test/t.pas $00401179 PLAY_WITH_STRINGS, line 23 of e:/c/test/t.pas $004011B1 main, line 28 of e:/c/test/t.pas However, if I include the sysutils unit, and catch (and reraise) all exceptions, I get the following -- which not only lacks the complete call stack, but includes lots of junk:

I get: (1.9.5 as of today)

-bash-2.05b$ ./exectest
An unhandled exception occurred at 0x0806CBFC :
EDivByZero : Division by zero
$0806CBFC TEST_DIVISION_BY_ZERO, line 14 of exectest.pp $0806CC81 PLAY_WITH_STRINGS, line 24 of exectest.pp $0806CCFF main, line 31 of exectest.pp

-bash-2.05b$

If I change all occurances of "string" to "ansistring", then:

-bash-2.05b$ ./exectest
An unhandled exception occurred at 0x0806CC1C :
EDivByZero : Division by zero
$0806CC1C TEST_DIVISION_BY_ZERO, line 14 of exectest.pp $BFBFF994 main, line 22 of /home2/marcov/cvs/devel/fpc/rtl/inc/heap.inc

Are you sure you compiled all units with -gl?

(compared to the first: notice exception handling is now working, and the third line has changed from "main, line 28" to line 30 -- where raise is called, rather than where play_with_strings is called)

it sounds like your sysutils is not compiled -gl, however it could also be a FPC version thing (I'm using a current versioN)

Peter Vreman
to David Emerson: We've got a sophisticated system which relies heavily on the power of ansistrings. I'm…
Show quoted text 15 lines
An unhandled exception occurred at 0x004010A8 : EDivByZero : Division by zero $004010A8 TEST_DIVISION_BY_ZERO, line 13 of e:/c/test/t.pas $666F2033 I noticed in my test program that if I change the ansistring_param to a normal string I get the call stack back: An unhandled exception occurred at 0x00401078 : EDivByZero : Division by zero $00401078 TEST_DIVISION_BY_ZERO, line 13 of e:/c/test/t.pas $004010F3 PLAY_WITH_STRINGS, line 23 of e:/c/test/t.pas $0040116B main, line 30 of e:/c/test/t.pas

The problem is that the call to the empty ansistring routine will create a temp ansistring for the parameter. And ansistring variables or temps need to be finalized. This call to finalize() destroyes the backtrace since it writes at the same location in the stack that was also used by test_division_by_zero. We can't solve easily.

David Emerson
to Marco van de Voort: I get: (1.9.5 as of today) -bash-2.05b$ ./exectest An unhandled exception occurred at…
Peter wrote 5 lines
The problem is that the call to the empty ansistring routine will create a temp ansistring for the parameter. And ansistring variables or temps need to be finalized. This call to finalize() destroyes the backtrace since it writes at the same location in the stack that was also used by test_division_by_zero. We can't solve easily.

I was afraid it might be something like this! Interestingly, even if I move the test_division_by_zero function to be BEFORE the call to the do_nothing_with_an_ansistring function, I still get the same behavior -- so it seems to be creating that temp ansistring param even before it gets to the call to the function that needs it. (( Perhaps this has something to do with threading or jumping or whatever you call it when the program starts a new task before it's finished with the old one in order to use resources efficiently. ))

Marco wrote 10 lines
I get: (1.9.5 as of today) -bash-2.05b$ ./exectest An unhandled exception occurred at 0x0806CBFC : EDivByZero : Division by zero $0806CBFC TEST_DIVISION_BY_ZERO, line 14 of exectest.pp $0806CC81 PLAY_WITH_STRINGS, line 24 of exectest.pp $0806CCFF main, line 31 of exectest.pp -bash-2.05b$

this is rather encouraging... however, I'm using 1.9.4, and am a little hesitant to step out on the bleeding edge of cvs with the project :-) but I may give it a shot.

Show quoted text 12 lines
If I change all occurances of "string" to "ansistring", then: -bash-2.05b$ ./exectest An unhandled exception occurred at 0x0806CC1C : EDivByZero : Division by zero $0806CC1C TEST_DIVISION_BY_ZERO, line 14 of exectest.pp $BFBFF994 main, line 22 of /home2/marcov/cvs/devel/fpc/rtl/inc/heap.inc Are you sure you compiled all units with -gl? (...) it sounds like your sysutils is not compiled -gl, however it could also be a FPC version thing (I'm using a current versioN)

I get the same behavior if I change all the strings to ansistrings (PLAY_WITH_STRINGS function call is missing from the stack trace.)

For the test program (and for my project) I always have the -gl flag in fpc.cfg. However I haven't compiled the sysutils unit, so I'm going to try that and see if it makes a difference. I'm a little skeptical due to Peter's comment, but the sources.tar just finished downloading, so I'll try my luck...

Thanks,
David

Peter Vreman
to David Emerson
Show quoted text 17 lines
Peter wrote: >The problem is that the call to the empty ansistring routine will create > a >temp ansistring for the parameter. And ansistring variables or temps need >to be finalized. This call to finalize() destroyes the backtrace since it >writes at the same location in the stack that was also used by >test_division_by_zero. We can't solve easily. I was afraid it might be something like this! Interestingly, even if I move the test_division_by_zero function to be BEFORE the call to the do_nothing_with_an_ansistring function, I still get the same behavior -- so it seems to be creating that temp ansistring param even before it gets to the call to the function that needs it. (( Perhaps this has something to do with threading or jumping or whatever you call it when the program starts a new task before it's finished with the old one in order to use resources efficiently. ))

It is not the creation of the temp ansistring, but the finalization at the end of the procedure. This finalization is always done, also when there was an exception.

David Emerson
to Marco van de Voort: I get: (1.9.5 as of today) -bash-2.05b$ ./exectest An unhandled exception occurred at…

Compiling sysutils with -gl gave the call stack seen below... insightful, I suppose, but not necessarily useful ;)

It definitely points to what Peter said about the ansistring finalize writing to the same place in the stack. Examining the stack, it seems that dump_stack calls get_caller_addr, and then I assume the access violation is about the address being unfindable since it's been overwritten.

1.9.5 (today's w32 binary) didn't work any better

re: Peter's last comment: so I guess the finalization of that temp ansistring is done at the end of play_with_strings, rather than being done within play_with_strings just after completion of the call to do_nothing_with_an_ansistring ... but it still irks me that the temp ansistring is created even if the function requiring it is not called. Oh well.

Has this been filed as a bug? (Is it considered a bug? I should hope so) ... for now, I guess I'm condemned to always be recompiling with a directive to either handle exceptions or see a call stack. I can live with that (as if I have a choice!)

Thanks, guys... keep up the good work, I'm sure this problem will be solved eventually :-)

~David.

Here's the stack with the debug version of sysutils (1.9.4):

An unhandled exception occurred at 0x004275D8 :
EDivByZero : Division by zero
$004275D8 TEST_DIVISION_BY_ZERO, line 13 of t.pas $702E7420 line 1 of windows.pp
An unhandled exception occurred at 0x00415B8F :
EAccessViolation : Access violation
$00415B8F GET_CALLER_ADDR, line 1207 of e:/c/194/src/rtl/i386/i386.inc $00420B58 DUMP_STACK, line 779 of e:/c/194/src/rtl/inc/system.inc $0066FDA0 line 1 of windows.pp
$702E7420 line 1 of windows.pp
An unhandled exception occurred at 0x00415B8F :
EAccessViolation : Access violation
$00415B8F GET_CALLER_ADDR, line 1207 of e:/c/194/src/rtl/i386/i386.inc $00420B58 DUMP_STACK, line 779 of e:/c/194/src/rtl/inc/system.inc $0066FB2C line 1 of windows.pp
$00420B58 DUMP_STACK, line 779 of e:/c/194/src/rtl/inc/system.inc $0066FDA0 line 1 of windows.pp
$702E7420 line 1 of windows.pp
An unhandled exception occurred at 0x00415B8F :
EAccessViolation : Access violation
$00415B8F GET_CALLER_ADDR, line 1207 of e:/c/194/src/rtl/i386/i386.inc $00420B58 DUMP_STACK, line 779 of e:/c/194/src/rtl/inc/system.inc $0066F8B8 line 1 of windows.pp
$00420B58 DUMP_STACK, line 779 of e:/c/194/src/rtl/inc/system.inc $0066FB2C line 1 of windows.pp
$00420B58 DUMP_STACK, line 779 of e:/c/194/src/rtl/inc/system.inc $0066FDA0 line 1 of windows.pp
$702E7420 line 1 of windows.pp
An unhandled exception occurred at 0x00415B8F :
EAccessViolation : Access violation
$00415B8F GET_CALLER_ADDR, line 1207 of e:/c/194/src/rtl/i386/i386.inc $00420B58 DUMP_STACK, line 779 of e:/c/194/src/rtl/inc/system.inc $0066F644 line 1 of windows.pp
$00420B58 DUMP_STACK, line 779 of e:/c/194/src/rtl/inc/system.inc $0066F8B8 line 1 of windows.pp
$00420B58 DUMP_STACK, line 779 of e:/c/194/src/rtl/inc/system.inc $0066FB2C line 1 of windows.pp
$00420B58 DUMP_STACK, line 779 of e:/c/194/src/rtl/inc/system.inc $0066FDA0 line 1 of windows.pp
$702E7420 line 1 of windows.pp

Peter Vreman
to David Emerson
you wrote 20 lines
Compiling sysutils with -gl gave the call stack seen below... insightful, I suppose, but not necessarily useful ;) It definitely points to what Peter said about the ansistring finalize writing to the same place in the stack. Examining the stack, it seems that dump_stack calls get_caller_addr, and then I assume the access violation is about the address being unfindable since it's been overwritten. 1.9.5 (today's w32 binary) didn't work any better re: Peter's last comment: so I guess the finalization of that temp ansistring is done at the end of play_with_strings, rather than being done within play_with_strings just after completion of the call to do_nothing_with_an_ansistring ... but it still irks me that the temp ansistring is created even if the function requiring it is not called. Oh well. Has this been filed as a bug? (Is it considered a bug? I should hope so) ... for now, I guess I'm condemned to always be recompiling with a directive to either handle exceptions or see a call stack. I can live with that (as if I have a choice!)

The quick fix for it will be to disable the dump_stack call after an unhandled exception is found.

Alternative solution is to retrieve the caller stack at the time an exception is raised. But that will take more cpu time and more memory.

Peter

David Emerson
to Peter Vreman
Peter wrote
The quick fix for it will be to disable the dump_stack call after an unhandled exception is found.

That gets rid of the junk, but I still lose the call stack.

Alternative solution is to retrieve the caller stack at the time an exception is raised. But that will take more cpu time and more memory.

Regarding the system resources -- it would only take that extra cpu time / memory at the time the exception is raised, correct?

(( Our own project does not raise exceptions extensively, but we do want to be able to handle them when they come up, rather than crash out. Maybe we will have one exception in five minutes of execution. ))

Also, I assume this would only work if I raise the exception. If I encounter, e.g., a stack overflow, I guess there wouldn't be a way to get the backtrace until the bug is taken care of? Or can I get it within my exception handling code before reraising?

I'm working on the "alternative solution" ... how do I get my hands on the Relevent Address and frame?

   Writeln (backtrace_text, BackTraceStrFunc(Addr -- ???) );
   Dump_Stack (backtrace_text, frame - ???);

Thanks,
David

Peter Vreman
to David Emerson
you wrote 27 lines
Peter wrote: >The quick fix for it will be to disable the dump_stack call after an unhandled exception is found. That gets rid of the junk, but I still lose the call stack. >Alternative solution is to retrieve the caller stack at the time an exception is raised. But that will take more cpu time and more memory. Regarding the system resources -- it would only take that extra cpu time / memory at the time the exception is raised, correct? (( Our own project does not raise exceptions extensively, but we do want to be able to handle them when they come up, rather than crash out. Maybe we will have one exception in five minutes of execution. )) Also, I assume this would only work if I raise the exception. If I encounter, e.g., a stack overflow, I guess there wouldn't be a way to get the backtrace until the bug is taken care of? Or can I get it within my exception handling code before reraising? I'm working on the "alternative solution" ... how do I get my hands on the Relevent Address and frame? Writeln (backtrace_text, BackTraceStrFunc(Addr -- ???) ); Dump_Stack (backtrace_text, frame - ???);

The exception handling routines are located in except.inc

In fpc_PushExceptObj the exception is raised. There is a structure PExceptObject that needs to be extended to contain a few (8?) caller addresses.

How to get the addresses of the frames can be found in dump_stack() located in system.inc

The unhandled exception code is in excepts.inc (rtl/objpas/sysutils/) Peter