July 30, 2004
Assuming that the function "catcher" has the catch block which will catch a certain exception, the stack will look something like this:

foo
  bar
    baz
      fred
        catcher
        <try-block>
          wilma
            barney
              function_with_error
                Error.this() <- ESP points here during
                                Error constructor


Now, when the error gets thrown, then the stack looks like this:

foo
  bar
    baz
      fred
        catcher
        <catch-block>

If you then call ANY function or create ANY local variables, then it looks like this:

foo
  bar
    baz
      fred
        catcher
        <catch-block>
          printStackTrace()

Notice that printStackTrace() now resides at the same place (more or less) as where the wilma function used to be.  So printStackTrace() is overwriting the data from wilma!  printStackTrace() is, by its very nature, corrupting the very stack that it is trying to dump.

That's why, if you want to do a stack dump in a catch block, you MUST MUST MUST duplicate the stack data before actually throwing the exception.  Once you've thrown your way out of a function frame, you can't know whether ANY of that frame is still valid.

July 30, 2004
Russ Lewis wrote:
> If you then call ANY function or create ANY local variables, then it looks like this:
> 
> foo
>   bar
>     baz
>       fred
>         catcher
>         <catch-block>
>           printStackTrace()
> 
> Notice that printStackTrace() now resides at the same place (more or less) as where the wilma function used to be.  So printStackTrace() is 

Yes I see what you mean. However all is not lost:
================================================================
   r-type fred( params ... ) {
           // ...
        try {
            // ...
             wilma() // Exception thrown
            // ...
        } catch( Exception e ) { e.printStackTrace(); }
    }
================================================================
This is what I consider the general case. Should this happen then (not counting the constructor issue I mentioned before) the only *write* to the stack /should/ be "call printStackTrace" which as I have will only write the return address which will be in fred.

That means you can inspect all the stack variables in wilma and you can find that wilma called barney and again see all the stack variables in barney. The same goes with function_with_error. You can walk way up to the Exception constructor.

Furthermore you can walk down the stack back to main. Sadly the only thing you /cannont/ do is figure out which function wilma is which is a rather important piece of information.

I just found that part of the exception handling is done with src/phobos/internal/deh2.d which has some helpful looking structs in it.

Given that D's exception handler has already run when you call printStackTrace() and that you can walk back up to the handler's info you might be able to add something like an array of all the return addresses on the way back down to main. With that information you would be able to get the information that is overwritten by a call to printStackTrace() and have it work as expected.
August 01, 2004
On Fri, 30 Jul 2004 18:26:55 -0400, parabolis <parabolis@softhome.net> wrote:
> Russ Lewis wrote:
>> If you then call ANY function or create ANY local variables, then it looks like this:
>>
>> foo
>>   bar
>>     baz
>>       fred
>>         catcher
>>         <catch-block>
>>           printStackTrace()
>>
>> Notice that printStackTrace() now resides at the same place (more or less) as where the wilma function used to be.  So printStackTrace() is
>
> Yes I see what you mean. However all is not lost:
> ================================================================
>     r-type fred( params ... ) {
>             // ...
>          try {
>              // ...
>               wilma() // Exception thrown
>              // ...
>          } catch( Exception e ) { e.printStackTrace(); }
>      }
> ================================================================
> This is what I consider the general case.

I don't agree. I think the general case is more like:

void main() {
  try {
    ..entire program code goes in here..
  } catch (Exception e) {
    ..catches any/all exceptions that are not caught and handled inside the program code..
    e.printStackTrace();
  }
}

> Should this happen then (not counting the constructor issue I mentioned before) the only *write* to the stack /should/ be "call printStackTrace" which as I have will only write the return address which will be in fred.
>
> That means you can inspect all the stack variables in wilma and you can find that wilma called barney and again see all the stack variables in barney. The same goes with function_with_error. You can walk way up to the Exception constructor.
>
> Furthermore you can walk down the stack back to main. Sadly the only thing you /cannont/ do is figure out which function wilma is which is a rather important piece of information.
>
> I just found that part of the exception handling is done with src/phobos/internal/deh2.d which has some helpful looking structs in it.
>
> Given that D's exception handler has already run when you call printStackTrace() and that you can walk back up to the handler's info you might be able to add something like an array of all the return addresses on the way back down to main. With that information you would be able to get the information that is overwritten by a call to printStackTrace() and have it work as expected.

Yes, I think actual compiler support can make this a trivial thing to implement. I was kinda hoping for the ability to say

catch (Exception e, Stack s) {
  ..use s to output the stack where e was thrown..
}

Regan

-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
August 01, 2004
Regan Heath wrote:
>> This is what I consider the general case.
> 
> 
> I don't agree. I think the general case is more like:
> 
> void main() {
>   try {
>     ..entire program code goes in here..
>   } catch (Exception e) {
>     ..catches any/all exceptions that are not caught and handled inside the program code..
>     e.printStackTrace();
>   }
> }

Sorry I should have been more clear. What I meant to get across with my general case example was that printStackTrace() would be immediately inside a catch block. Your example agrees with what I was assuming the general case would be. The case where printStackStrace() might have odd performance is a case in the stack is modified before calling printStackTrace().

> 
> 
> Yes, I think actual compiler support can make this a trivial thing to implement. I was kinda hoping for the ability to say
> 
> catch (Exception e, Stack s) {
>   ..use s to output the stack where e was thrown..
> }
> 
That is an interesting suggestion.
August 01, 2004
In article <ceehvn$6qm$1@digitaldaemon.com>, parabolis says...

>================================================================ This is what I consider the general case. Should this happen then (not counting the constructor issue I mentioned before) the only *write* to the stack /should/ be "call printStackTrace" which as I have will only write the return address which will be in fred.

I think you may be forgetting that an exception (in the X86 sense, not in the D sense) could be thrown at any time. For example, an interrupt is a hardware exception, and one of these will be thrown every few milliseconds to determine whether or not a process-switch is required. When this happens, every register (and a few other things besides) are backed up onto the stack. In general, an (X86) exception handler can utilise arbitrary amounts of stack space.

In summary, everything beyond the top of the stack must always be assumed to be corrupt.



August 01, 2004
Arcane Jill wrote:

> I think you may be forgetting that an exception (in the X86 sense, not in the D
> sense) could be thrown at any time. For example, an interrupt is a hardware
> exception, and one of these will be thrown every few milliseconds to determine
> whether or not a process-switch is required. When this happens, every register
> (and a few other things besides) are backed up onto the stack. In general, an
> (X86) exception handler can utilise arbitrary amounts of stack space.
> 
> In summary, everything beyond the top of the stack must always be assumed to be
> corrupt.
> 

I am not forgetting these process switches are thrown. I was (wrongly I take it) assuming that such contect switches would be handled by preallocating space at the bottom of the stack when a process is created explicitly for storing registers during context switches.

That is exacactly the sort of information I am most thankful to hear. I will have to look into this. Thanks!
1 2 3 4 5 6
Next ›   Last »