www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - prog segfaults

reply Ant <duitoolkit yahoo.ca> writes:
(for sure I'm overseeing something)

bug report (?) should I post on the bugs group?

I was trying to confirm we need to initialize objects 
pass with "out" and the executable for this just segfaults:

#############
class A
{
	int a;
}

void f(out A a)
{
	assert(a); // OK IF REPLACE BY assert(a!==null);
	printf("hello\n");
}

void main()
{
	A a = new A();
	f(a);
}
##############

Ant





















here is the 10 lines version:
class A{int a;}

void f(out A a)
{
	assert(a);
	printf("hello\n");
}

void main()
{A a = new A();f(a);}
Jul 27 2004
parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 27 Jul 2004 21:09:07 -0400, Ant wrote:

 class A
 {
 	int a;
 }
 
 void f(out A a)
 {
 	assert(a); // OK IF REPLACE BY assert(a!==null);
 	printf("hello\n");
 }
 
 void main()
 {
 	A a = new A();
 	f(a);
 }

When function 'f' gets its parameters, the 'a' will be null because of the 'out' option. Even though you passed a new instance of A to it, the out option will initialize it to null. The assert is probably redundant as this is the expected behaviour of the language. Try this code, it should crash with an access violation on the second writef() call because the f() call's out option initialized it. <code> import std.stdio; class A { int x; } void f(out A a) { assert(a is null); writef("hello\n"); } void main() { A a = new A; a.x = 1; writef("before= %d\n", a.x); f(a); writef("after = %d\n", a.x); } </code> -- Derek Melbourne, Australia 28/Jul/04 12:12:07 PM
Jul 27 2004
parent reply Ant <duitoolkit yahoo.ca> writes:
On Wed, 28 Jul 2004 12:18:37 +1000, Derek Parnell wrote:

 On Tue, 27 Jul 2004 21:09:07 -0400, Ant wrote:
 
 class A
 {
 	int a;
 }
 
 void f(out A a)
 {
 	assert(a); // OK IF REPLACE BY assert(a!==null);
 	printf("hello\n");
 }
 
 void main()
 {
 	A a = new A();
 	f(a);
 }

When function 'f' gets its parameters, the 'a' will be null because of the 'out' option. Even though you passed a new instance of A to it, the out option will initialize it to null. The assert is probably redundant as this is the expected behaviour of the language.

yes, that's what I was trying to confirm. but the program shouldn't segfault! it should print and assertion failure. (?) Ant
Jul 27 2004
parent reply Andy Friesen <andy ikagames.com> writes:
Ant wrote:
 On Wed, 28 Jul 2004 12:18:37 +1000, Derek Parnell wrote:
 
void f(out A a)
{
	assert(a); // OK IF REPLACE BY assert(a!==null);
	printf("hello\n");
}

'out' option. Even though you passed a new instance of A to it, the out option will initialize it to null. The assert is probably redundant as this is the expected behaviour of the language.

yes, that's what I was trying to confirm. but the program shouldn't segfault! it should print and assertion failure. (?)

assert(a); is a special case in the language: it is not a boolean not-null test, it runs A's invariant to assert that 'a' is correct. Still, you would think that an assertion failure would trip, since part of Object.invariant is "assert(this!==null);" -- andy
Jul 27 2004
parent reply Ant <duitoolkit yahoo.ca> writes:
On Tue, 27 Jul 2004 20:02:06 -0700, Andy Friesen wrote:

 
 assert(a); is a special case in the language: it is not a boolean 
 not-null test, it runs A's invariant to assert that 'a' is correct.

I didn't know that, cheking documentation ... checked, good!
 
 Still, you would think that an assertion failure would trip, since part 
 of Object.invariant is "assert(this!==null);"
 

so looks like a bug, smells like a bug, is it a bug? Ant
Jul 27 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Ant" <duitoolkit yahoo.ca> wrote in message
news:pan.2004.07.28.03.06.34.780054 yahoo.ca...
 On Tue, 27 Jul 2004 20:02:06 -0700, Andy Friesen wrote:

 assert(a); is a special case in the language: it is not a boolean
 not-null test, it runs A's invariant to assert that 'a' is correct.

I didn't know that, cheking documentation ... checked, good!
 Still, you would think that an assertion failure would trip, since part
 of Object.invariant is "assert(this!==null);"

so looks like a bug, smells like a bug, is it a bug?

No. assert(a) doesn't bother checking to see if a is null before calling the invariant, because the hardware will do that for you. Why duplicate what the hardware does?
Jul 27 2004
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 27 Jul 2004 20:41:56 -0700, Walter wrote:

 "Ant" <duitoolkit yahoo.ca> wrote in message
 news:pan.2004.07.28.03.06.34.780054 yahoo.ca...
 On Tue, 27 Jul 2004 20:02:06 -0700, Andy Friesen wrote:

 assert(a); is a special case in the language: it is not a boolean
 not-null test, it runs A's invariant to assert that 'a' is correct.

I didn't know that, cheking documentation ... checked, good!
 Still, you would think that an assertion failure would trip, since part
 of Object.invariant is "assert(this!==null);"

so looks like a bug, smells like a bug, is it a bug?

No. assert(a) doesn't bother checking to see if a is null before calling the invariant, because the hardware will do that for you. Why duplicate what the hardware does?

Because assert() gives us filename and line number, but "Access Violation" doesn't. -- Derek Melbourne, Australia 28/Jul/04 2:29:38 PM
Jul 27 2004
next sibling parent Regan Heath <regan netwin.co.nz> writes:
On Wed, 28 Jul 2004 14:35:05 +1000, Derek Parnell <derek psych.ward> wrote:
 On Tue, 27 Jul 2004 20:41:56 -0700, Walter wrote:

 "Ant" <duitoolkit yahoo.ca> wrote in message
 news:pan.2004.07.28.03.06.34.780054 yahoo.ca...
 On Tue, 27 Jul 2004 20:02:06 -0700, Andy Friesen wrote:

 assert(a); is a special case in the language: it is not a boolean
 not-null test, it runs A's invariant to assert that 'a' is correct.

I didn't know that, cheking documentation ... checked, good!
 Still, you would think that an assertion failure would trip, since 
 part
 of Object.invariant is "assert(this!==null);"

so looks like a bug, smells like a bug, is it a bug?

No. assert(a) doesn't bother checking to see if a is null before calling the invariant, because the hardware will do that for you. Why duplicate what the hardware does?

Because assert() gives us filename and line number, but "Access Violation" doesn't.

Good point, in fact in debug mode I want all errors to give a file and line number, including access violation. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 27 2004
prev sibling next sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Derek Parnell" <derek psych.ward> wrote in message
news:ce7aei$2une$1 digitaldaemon.com...
 On Tue, 27 Jul 2004 20:41:56 -0700, Walter wrote:

 "Ant" <duitoolkit yahoo.ca> wrote in message
 news:pan.2004.07.28.03.06.34.780054 yahoo.ca...
 On Tue, 27 Jul 2004 20:02:06 -0700, Andy Friesen wrote:

 assert(a); is a special case in the language: it is not a boolean
 not-null test, it runs A's invariant to assert that 'a' is correct.

I didn't know that, cheking documentation ... checked, good!
 Still, you would think that an assertion failure would trip, since




 of Object.invariant is "assert(this!==null);"

so looks like a bug, smells like a bug, is it a bug?

No. assert(a) doesn't bother checking to see if a is null before calling


 invariant, because the hardware will do that for you. Why duplicate what


 hardware does?

Because assert() gives us filename and line number, but "Access Violation" doesn't.

Run it under the debugger, and the debugger will open up a window and put the cursor on it <g>.
Jul 27 2004
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 27 Jul 2004 22:53:31 -0700, Walter wrote:

 "Derek Parnell" <derek psych.ward> wrote in message
 news:ce7aei$2une$1 digitaldaemon.com...
 On Tue, 27 Jul 2004 20:41:56 -0700, Walter wrote:

 "Ant" <duitoolkit yahoo.ca> wrote in message
 news:pan.2004.07.28.03.06.34.780054 yahoo.ca...
 On Tue, 27 Jul 2004 20:02:06 -0700, Andy Friesen wrote:

 assert(a); is a special case in the language: it is not a boolean
 not-null test, it runs A's invariant to assert that 'a' is correct.

I didn't know that, cheking documentation ... checked, good!
 Still, you would think that an assertion failure would trip, since




 of Object.invariant is "assert(this!==null);"

so looks like a bug, smells like a bug, is it a bug?

No. assert(a) doesn't bother checking to see if a is null before calling


 invariant, because the hardware will do that for you. Why duplicate what


 hardware does?

Because assert() gives us filename and line number, but "Access Violation" doesn't.

Run it under the debugger, and the debugger will open up a window and put the cursor on it <g>.

Sorry, too glib. Not impressed. Why bother having assert give this info? And which "the debugger" are you referring to? -- Derek Melbourne, Australia 28/Jul/04 4:40:23 PM
Jul 27 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Derek Parnell" <derek psych.ward> wrote in message
news:ce7ijg$30vm$1 digitaldaemon.com...
 Because assert() gives us filename and line number, but "Access



 doesn't.

Run it under the debugger, and the debugger will open up a window and


 the cursor on it <g>.

Sorry, too glib. Not impressed. Why bother having assert give this info?

Because debuggers don't work off of asserts.
 And which "the debugger" are you referring to?

Most any debugger will do it. In fact, it's one of the main features of a semi-decent debugger. I can't remember any protected mode debugger that didn't do it. Debuggers will also give a stack trace, which is so useful that sometimes I'll replace the assert with a *(char*)0=0; just to run it under a debugger to get the stack trace. GPFs have an undeservedly bad rap. There's nothing inherently worse about it than an assert failure. They're really the same thing, and the GPF has the nice feature that you get such checking with 0 extra overhead (unlike asserts). The GPF does in hardware what the assert does in software, that's all.
Jul 28 2004
parent reply Daniel Horn <hellcatv hotmail.com> writes:
can't assert print the stack (if you have the frame pointer)?
I mean you just need some logic that does the same thing that the 
debugger would do...and call that on an assert...
it's useless to have an assert not throw a signal otherwise...usually 
the assert isn't where the problem is occuring, but n frame(s) above it.


Walter wrote:
 "Derek Parnell" <derek psych.ward> wrote in message
 news:ce7ijg$30vm$1 digitaldaemon.com...
 
Because assert() gives us filename and line number, but "Access



Violation"
doesn't.

Run it under the debugger, and the debugger will open up a window and


put
the cursor on it <g>.

Sorry, too glib. Not impressed. Why bother having assert give this info?

Because debuggers don't work off of asserts.
And which "the debugger" are you referring to?

Most any debugger will do it. In fact, it's one of the main features of a semi-decent debugger. I can't remember any protected mode debugger that didn't do it. Debuggers will also give a stack trace, which is so useful that sometimes I'll replace the assert with a *(char*)0=0; just to run it under a debugger to get the stack trace. GPFs have an undeservedly bad rap. There's nothing inherently worse about it than an assert failure. They're really the same thing, and the GPF has the nice feature that you get such checking with 0 extra overhead (unlike asserts). The GPF does in hardware what the assert does in software, that's all.

Jul 28 2004
parent "Walter" <newshound digitalmars.com> writes:
"Daniel Horn" <hellcatv hotmail.com> wrote in message
news:ce8nrb$es2$2 digitaldaemon.com...
 can't assert print the stack (if you have the frame pointer)?
 I mean you just need some logic that does the same thing that the
 debugger would do...and call that on an assert...
 it's useless to have an assert not throw a signal otherwise...usually
 the assert isn't where the problem is occuring, but n frame(s) above it.

It's not so easy since the debug data needed is sometimes separate. The debugger has the debug data, and so can do a stack trace easilly. That's why I sometimes replace an assert with *(char*)0=0;.
Jul 28 2004
prev sibling parent reply Ant <Ant_member pathlink.com> writes:
In article <ce7fjr$30a6$1 digitaldaemon.com>, Walter says...
Run it under the debugger, and the debugger will open up a window and put
the cursor on it <g>.

Is it possible under linux? (I know I asked this before and nobody answer, I promiss I won't ask again) Walter sais he uses gdb but I can't convince it to show the D source (no problem with C source). Ant
Jul 28 2004
next sibling parent reply Ben Hinkle <bhinkle4 juno.com> writes:
Ant wrote:

 In article <ce7fjr$30a6$1 digitaldaemon.com>, Walter says...
Run it under the debugger, and the debugger will open up a window and put
the cursor on it <g>.

Is it possible under linux? (I know I asked this before and nobody answer, I promiss I won't ask again) Walter sais he uses gdb but I can't convince it to show the D source (no problem with C source). Ant

I've been using gdb but I've noticed 1) the source doesn't show up - annoying but not a big deal for me since I don't have much code 2) sometimes the stack traces printed out by "bt" look pretty messed up even for simple things 3) I can't look at variables when stopped
Jul 28 2004
parent reply Ant <Ant_member pathlink.com> writes:
In article <ce86dc$7pd$1 digitaldaemon.com>, Ben Hinkle says...
I've been using gdb but I've noticed 
1) the source doesn't show up - annoying but not a big deal for me since I
don't have much code
2) sometimes the stack traces printed out by "bt" look pretty messed up even
for simple things
3) I can't look at variables when stopped

So, why do you use it? What can you get? (It's an honest question. not a joke) What do you get from it? I've been using it (with DDD front end) to debug the interface with C (on DUI) but it doesn't help me with D. Ant
Jul 28 2004
parent reply Ben Hinkle <bhinkle4 juno.com> writes:
Ant wrote:

 In article <ce86dc$7pd$1 digitaldaemon.com>, Ben Hinkle says...
I've been using gdb but I've noticed
1) the source doesn't show up - annoying but not a big deal for me since I
don't have much code
2) sometimes the stack traces printed out by "bt" look pretty messed up
even for simple things
3) I can't look at variables when stopped

So, why do you use it? What can you get? (It's an honest question. not a joke) What do you get from it?

usually the stack trace is correct. that's all I use it for.
 I've been using it (with DDD front end) to debug the interface with
 C (on DUI) but it doesn't help me with D.
 
 Ant

Jul 28 2004
parent Ant <duitoolkit yahoo.ca> writes:
On Wed, 28 Jul 2004 12:22:35 -0400, Ben Hinkle wrote:

 
 usually the stack trace is correct. that's all I use it for.
  

thanks, Ben and Walter and guys I can see the stack! :) (finally :p) Ant
Jul 28 2004
prev sibling parent "Walter" <newshound digitalmars.com> writes:
"Ant" <Ant_member pathlink.com> wrote in message
news:ce83ht$6in$1 digitaldaemon.com...
 In article <ce7fjr$30a6$1 digitaldaemon.com>, Walter says...
Run it under the debugger, and the debugger will open up a window and put
the cursor on it <g>.

Is it possible under linux? (I know I asked this before and nobody answer, I promiss I won't ask

 Walter sais he uses gdb but I can't convince it to show the
 D source (no problem with C source).

That's because I've been lazy and have not gotten the debug data written out correctly in the elf format. gdb will, however, give the stack trace, and with the 'disassemble' command will show you which instruction failed, which if you turn optimization off is pretty easy to spot which line it is on.
Jul 28 2004
prev sibling parent reply Nick <Nick_member pathlink.com> writes:
Dixit Derek Parnell says:

On Tue, 27 Jul 2004 20:41:56 -0700, Walter wrote:
 
 No. assert(a) doesn't bother checking to see if a is null before calling the
 invariant, because the hardware will do that for you. Why duplicate what the
 hardware does?

Because assert() gives us filename and line number, but "Access Violation" doesn't.

Also, you can try/catch the assert if you're so inclined. Nick
Jul 28 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Nick" <Nick_member pathlink.com> wrote in message
news:ce8ubj$if6$1 digitaldaemon.com...
 Dixit Derek Parnell says:

On Tue, 27 Jul 2004 20:41:56 -0700, Walter wrote:
 No. assert(a) doesn't bother checking to see if a is null before



 invariant, because the hardware will do that for you. Why duplicate



 hardware does?

Because assert() gives us filename and line number, but "Access


doesn't.

Also, you can try/catch the assert if you're so inclined.

You can catch the access violations, too!
Jul 28 2004
parent reply Nick <dont like.spam> writes:
Walter wrote:
Also, you can try/catch the assert if you're so inclined.

You can catch the access violations, too!

Well, it doesn't seem to work for me... (using linux). Running the following just gives a segmentation fault: import std.stdio; class A {} void main() { A a; try { assert(a); } catch { writefln("catch"); } } Perhaps it's just a linux bug? Nick
Jul 28 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Nick wrote:
 Walter wrote:
 
 Also, you can try/catch the assert if you're so inclined.

You can catch the access violations, too!

Well, it doesn't seem to work for me... (using linux). Running the following just gives a segmentation fault: import std.stdio; class A {} void main() { A a; try { assert(a); } catch { writefln("catch"); } } Perhaps it's just a linux bug?

No. The problem here is exactly the "feature" that Walter is using. Your assert is failing because of a segmentation fault, because the line is attempting to read data at address 0. A segmentation fault is caught by the operating system, not the program, an as such doesn't call under the try/catch paradigm. To catch a segfault in linux, you have to register a signal handler for that. See "man signal" or "man sigaction". I wonder if you can throw an exception inside of a signal handler, and have it caught by the failing code? Something like this: * WARNING WARNING WARNING * * I have no idea if this would work... * void main() { register_signal_handler(SIG_SEGV, &segfault_handler); A a; try { assert(a); } catch(SegFaultException e) { // whatever } } void segfault_handler() { throw new SegFaultException(); }
Jul 28 2004
parent "Walter" <newshound digitalmars.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:ce94kf$kti$1 digitaldaemon.com...
 Nick wrote:
 Walter wrote:

 Also, you can try/catch the assert if you're so inclined.

You can catch the access violations, too!

Well, it doesn't seem to work for me... (using linux). Running the following just gives a segmentation fault: import std.stdio; class A {} void main() { A a; try { assert(a); } catch { writefln("catch"); } } Perhaps it's just a linux bug?

No. The problem here is exactly the "feature" that Walter is using. Your assert is failing because of a segmentation fault, because the line is attempting to read data at address 0. A segmentation fault is caught by the operating system, not the program, an as such doesn't call under the try/catch paradigm. To catch a segfault in linux, you have to register a signal handler for that. See "man signal" or "man sigaction". I wonder if you can throw an exception inside of a signal handler, and have it caught by the failing code? Something like this: * WARNING WARNING WARNING * * I have no idea if this would work... * void main() { register_signal_handler(SIG_SEGV, &segfault_handler); A a; try { assert(a); } catch(SegFaultException e) { // whatever } } void segfault_handler() { throw new SegFaultException(); }

The catch will work under Windows, I just haven't implemented the linux end. I think your scheme will work.
Jul 28 2004
prev sibling parent reply Ant <Ant_member pathlink.com> writes:
In article <ce77fg$2sh7$1 digitaldaemon.com>, Walter says...
"Ant" <duitoolkit yahoo.ca> wrote in message
news:pan.2004.07.28.03.06.34.780054 yahoo.ca...
 On Tue, 27 Jul 2004 20:02:06 -0700, Andy Friesen wrote:

 assert(a); is a special case in the language: it is not a boolean
 not-null test, it runs A's invariant to assert that 'a' is correct.

I didn't know that, cheking documentation ... checked, good!
 Still, you would think that an assertion failure would trip, since part
 of Object.invariant is "assert(this!==null);"

so looks like a bug, smells like a bug, is it a bug?

No. assert(a) doesn't bother checking to see if a is null before calling the invariant, because the hardware will do that for you. Why duplicate what the hardware does?

To give a line number and file name to the poor guy that spends hours looking at his or someone else's code? Ant
Jul 28 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Ant" <Ant_member pathlink.com> wrote in message
news:ce838c$6fe$1 digitaldaemon.com...
 In article <ce77fg$2sh7$1 digitaldaemon.com>, Walter says...
No. assert(a) doesn't bother checking to see if a is null before calling


invariant, because the hardware will do that for you. Why duplicate what


hardware does?

To give a line number and file name to the poor guy that spends hours looking at his or someone else's code?

The debugger can do that for you.
Jul 28 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Wed, 28 Jul 2004 13:04:12 -0700, Walter <newshound digitalmars.com> 
wrote:
 "Ant" <Ant_member pathlink.com> wrote in message
 news:ce838c$6fe$1 digitaldaemon.com...
 In article <ce77fg$2sh7$1 digitaldaemon.com>, Walter says...
No. assert(a) doesn't bother checking to see if a is null before 


invariant, because the hardware will do that for you. Why duplicate 


hardware does?

To give a line number and file name to the poor guy that spends hours looking at his or someone else's code?

The debugger can do that for you.

Ok, but, now assume the piece of software is a mail server, which you have written, which is running on a large production system. It crashes due to a bug which is not reproducable due to it's specific nature, i.e. under heavy load on that specific operating system etc. You cannot use a debugger to find this bug. You could use one on a core _if_ it dropped one (it is common for systems to be configured not to drop one - this is experience talking) What you ideally want is for your application to do it's own debugging. On the event of an assert or Access Violation you catch it, and write a log file containing the file and line number, a complete stack trace, and excerpts from the various log files it writes, you notify the watching process and die. The watcher grabs the crash log, emails it to the sysadmin, and restarts the mail server. Is all that possible with D currently? I believe most of it is, I don't think I can get the __FILE__ and __LINE__ data. A nice cross platform stack trace lib would be ideal. A nice cross platform assert and access violation (plus other signals) catching lib would be ideal. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 28 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message
news:opsbvck2ao5a2sq9 digitalmars.com...
 To give a line number and file name to the poor guy that spends hours
 looking at his or someone else's code?

The debugger can do that for you.

Ok, but, now assume the piece of software is a mail server, which you have written, which is running on a large production system. It crashes due to a bug which is not reproducable due to it's specific nature, i.e. under heavy load on that specific operating system etc. You cannot use a debugger to find this bug. You could use one on a core _if_ it dropped one (it is common for systems to be configured not to drop one - this is experience talking) What you ideally want is for your application to do it's own debugging. On the event of an assert or Access Violation you catch it, and write a log file containing the file and line number, a complete stack trace, and excerpts from the various log files it writes, you notify the watching process and die. The watcher grabs the crash log, emails it to the sysadmin, and restarts the mail server. Is all that possible with D currently? I believe most of it is, I don't think I can get the __FILE__ and __LINE__ data.

Yes. First of all, you can catch access violations with the catch clause (works under Windows, I haven't made it work under linux yet). Next, given the address of where it faulted, it can be compared with the line number info in the program's debug data to get the file/line. Normally, the debugger does this for you, but there's no reason one cannot do it with a specially written chunk of code that reads the debug data.
Jul 28 2004
next sibling parent reply Regan Heath <regan netwin.co.nz> writes:
On Wed, 28 Jul 2004 19:33:48 -0700, Walter <newshound digitalmars.com> 
wrote:

 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opsbvck2ao5a2sq9 digitalmars.com...
 To give a line number and file name to the poor guy that spends hours
 looking at his or someone else's code?

The debugger can do that for you.

Ok, but, now assume the piece of software is a mail server, which you have written, which is running on a large production system. It crashes due to a bug which is not reproducable due to it's specific nature, i.e. under heavy load on that specific operating system etc. You cannot use a debugger to find this bug. You could use one on a core _if_ it dropped one (it is common for systems to be configured not to drop one - this is experience talking) What you ideally want is for your application to do it's own debugging. On the event of an assert or Access Violation you catch it, and write a log file containing the file and line number, a complete stack trace, and excerpts from the various log files it writes, you notify the watching process and die. The watcher grabs the crash log, emails it to the sysadmin, and restarts the mail server. Is all that possible with D currently? I believe most of it is, I don't think I can get the __FILE__ and __LINE__ data.

Yes. First of all, you can catch access violations with the catch clause (works under Windows, I haven't made it work under linux yet).

Cool. Is "access violation" == SIGSEGV Does it catch anything else as well? It would be cool if I could catch: #define SIGINT 2 /* interrupt */ #define SIGILL 4 /* illegal instruction - invalid function image */ #define SIGFPE 8 /* floating point exception */ #define SIGSEGV 11 /* segment violation */ #define SIGTERM 15 /* Software termination signal from kill */ #define SIGBREAK 21 /* Ctrl-Break sequence */ #define SIGABRT 22 /* abnormal termination triggered by abort call */ (the above are just the windows signals) If try/catch and exceptions are to be the error handling mechanism it makes sense that you can do all your error handling this way, including signals, access violations, asserts, etc.
 Next, given
 the address of where it faulted, it can be compared with the line number
 info in the program's debug data to get the file/line. Normally, the
 debugger does this for you, but there's no reason one cannot do it with a
 specially written chunk of code that reads the debug data.

Great, but, I have no idea how to do that. So either: - I have to wait till someone else does it and releases it. - I have to learn how. or the third option, my favourite: - The compiler defines a 'char[] file' and 'uint line' which I can access. Why is that so hard to do? try { } catch { //access violation printf("Access Violation: %.*s:%d\n",file,line); } Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 28 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message
news:opsbvo8cpg5a2sq9 digitalmars.com...
 Cool. Is "access violation" == SIGSEGV

 Does it catch anything else as well? It would be cool if I could catch:

 #define SIGINT          2       /* interrupt */
 #define SIGILL          4       /* illegal instruction - invalid function
 image */
 #define SIGFPE          8       /* floating point exception */
 #define SIGSEGV         11      /* segment violation */
 #define SIGTERM         15      /* Software termination signal from kill

 #define SIGBREAK        21      /* Ctrl-Break sequence */
 #define SIGABRT         22      /* abnormal termination triggered by abort
 call */

 (the above are just the windows signals)

Yes. If you want to see how it's done, see internal\deh.c function _d_translate_se_to_d_exception()
 or the third option, my favourite:
   - The compiler defines a 'char[] file' and 'uint line' which I can

 Why is that so hard to do?

I always thought __FILE__ and __LINE__ are just so ugly. But when you think about it, __FILE__ and __LINE__ only really make sense for macros. Using them directly is pointless, isn't it? Wouldn't it make sense to throw a string, and then grep on that string if you can't remember where it came from? You can also simply write: assert(p != null); and not generate a seg fault.
Jul 28 2004
next sibling parent reply "Mickey Finn" <horizon blackhole.net> writes:
"Walter" <newshound digitalmars.com> wrote in message
news:cea6oh$14c3$1 digitaldaemon.com...
 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opsbvo8cpg5a2sq9 digitalmars.com...
 Cool. Is "access violation" == SIGSEGV

 Does it catch anything else as well? It would be cool if I could catch:

 #define SIGINT          2       /* interrupt */
 #define SIGILL          4       /* illegal instruction - invalid


 image */
 #define SIGFPE          8       /* floating point exception */
 #define SIGSEGV         11      /* segment violation */
 #define SIGTERM         15      /* Software termination signal from kill

 #define SIGBREAK        21      /* Ctrl-Break sequence */
 #define SIGABRT         22      /* abnormal termination triggered by


 call */

 (the above are just the windows signals)

Yes. If you want to see how it's done, see internal\deh.c function _d_translate_se_to_d_exception()
 or the third option, my favourite:
   - The compiler defines a 'char[] file' and 'uint line' which I can

 Why is that so hard to do?

I always thought __FILE__ and __LINE__ are just so ugly. But when you

 about it, __FILE__ and __LINE__ only really make sense for macros. Using
 them directly is pointless, isn't it? Wouldn't it make sense to throw a
 string, and then grep on that string if you can't remember where it came
 from?

1) Sometimes the same exception message is used in multiple places, so it can be hard to track down. 2) Sometimes you don't have the source, but still need to know the culprit 3) the message itself might be manufactured (via localization/externalization) so wouldn't be found with a grep 4) it's very useful to place this information into a 'log' message, so you can easily see what produced a message witthout having to scan the source. etc. If you're concerned about exposing the __FILE__ as a symbol, is it possible to wrap it? Perhaps as an instrinsic function (like rol/ror)?
Jul 29 2004
parent "Mickey Finn" <horizon blackhole.net> writes:
 1) Sometimes the same exception message is used in multiple places, so it
 can be hard to track down.
 2) Sometimes you don't have the source, but still need to know the culprit
 3) the message itself might be manufactured (via
 localization/externalization) so wouldn't be found with a grep
 4) it's very useful to place this information into a 'log' message, so you
 can easily see what produced a message witthout having to scan the source.

 etc.

5) dmd.exe uses __FILE__ & __LINE__ itself: I just got this message from the compiler :-) Internal error: s2ir.c 457
Jul 29 2004
prev sibling next sibling parent reply Farmer <itsFarmer. freenet.de> writes:
"Walter" <newshound digitalmars.com> wrote in 
news:cea6oh$14c3$1 digitaldaemon.com:

 
 You can also simply write:
     assert(p != null);
 and not generate a seg fault.

Sure it does! (I checked with DMD 0.96) You probably meant: assert(p isnot null); // ;-) Joking aside, exception stacktraces would be a boon to programmers. The pain with exceptions is that in well refactored code, exceptions are often thrown in on function, but that function is called via various code paths. So you know where the exception was thrown and where it was caught, but you don't known what happened in between. I for one, refused to use exceptions in C++ mainly for that reason. And the one time, I did use exceptions, I actually hacked the stacktrace in my code by hand and a little help from the preprocessor. The __FILE__, __LINE__ and especially __FUNCTION__ (non-standard) data is very handy for writting log files. Please, consider that log files are *very* important to some people, so they create all this information by hand if a language doesn't provide it or in Java's case cannot produce it fast enough. Farmer.
Jul 29 2004
parent reply Sean Kelly <sean f4.ca> writes:
In article <Xns9536AC639A46itsFarmer 63.105.9.61>, Farmer says...
So you know where the exception was thrown and where it was caught, but you 
don't known what happened in between.
I for one, refused to use exceptions in C++ mainly for that reason. And the 
one time, I did use exceptions, I actually hacked the stacktrace in my code  
by hand and a little help from the preprocessor. 

There have been articles written by very smart people on how to (in code) generate stack traces in C++ when exceptions are thrown. IIRC the conclusion was that it's just not possible to do very well. I agree that it would be nice to have compiler support for stack traces when exceptions are thrown. Sean
Jul 29 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Sean Kelly wrote:
 In article <Xns9536AC639A46itsFarmer 63.105.9.61>, Farmer says...
 
So you know where the exception was thrown and where it was caught, but you 
don't known what happened in between.
I for one, refused to use exceptions in C++ mainly for that reason. And the 
one time, I did use exceptions, I actually hacked the stacktrace in my code  
by hand and a little help from the preprocessor. 

There have been articles written by very smart people on how to (in code) generate stack traces in C++ when exceptions are thrown. IIRC the conclusion was that it's just not possible to do very well. I agree that it would be nice to have compiler support for stack traces when exceptions are thrown.

It is possible to do a portable stack trace if you know the starting address and size of every function in the program. That is something that could be provided in a portable way by the compiler. Something like this: struct FunctionInfo { char[] name; // name would include module name, and, for nested // or literal functions, the nested function name. // Something like: // foo.bar.baz.MyFunc(int).nestedFunc1(char) char[] linkname; // this would be the C-compatible name, as it is // in the object file byte *startingAddr; size_t size; FunctionInfo[] subFunctions; // nested functions and literal functions go here }; FunctionInfo[] GetAllFunctions() {...}
Jul 29 2004
parent Regan Heath <regan netwin.co.nz> writes:
On Thu, 29 Jul 2004 21:48:00 -0700, Russ Lewis 
<spamhole-2001-07-16 deming-os.org> wrote:

 Sean Kelly wrote:
 In article <Xns9536AC639A46itsFarmer 63.105.9.61>, Farmer says...

 So you know where the exception was thrown and where it was caught, 
 but you don't known what happened in between.
 I for one, refused to use exceptions in C++ mainly for that reason. 
 And the one time, I did use exceptions, I actually hacked the 
 stacktrace in my code  by hand and a little help from the preprocessor.

There have been articles written by very smart people on how to (in code) generate stack traces in C++ when exceptions are thrown. IIRC the conclusion was that it's just not possible to do very well. I agree that it would be nice to have compiler support for stack traces when exceptions are thrown.

It is possible to do a portable stack trace if you know the starting address and size of every function in the program. That is something that could be provided in a portable way by the compiler. Something like this: struct FunctionInfo { char[] name; // name would include module name, and, for nested // or literal functions, the nested function name. // Something like: // foo.bar.baz.MyFunc(int).nestedFunc1(char) char[] linkname; // this would be the C-compatible name, as it is // in the object file byte *startingAddr; size_t size; FunctionInfo[] subFunctions; // nested functions and literal functions go here }; FunctionInfo[] GetAllFunctions() {...}

I think some little help by the compiler would go a long way to making a stack trace nice and easy to implement. Then someone could write one, put it in phobos, and I could use it (I dont need it right now, I just know I will need it when I write commercial server software in D) Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 29 2004
prev sibling parent Regan Heath <regan netwin.co.nz> writes:
On Wed, 28 Jul 2004 23:43:33 -0700, Walter <newshound digitalmars.com> 
wrote:
 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opsbvo8cpg5a2sq9 digitalmars.com...
 Cool. Is "access violation" == SIGSEGV

 Does it catch anything else as well? It would be cool if I could catch:

 #define SIGINT          2       /* interrupt */
 #define SIGILL          4       /* illegal instruction - invalid 
 function
 image */
 #define SIGFPE          8       /* floating point exception */
 #define SIGSEGV         11      /* segment violation */
 #define SIGTERM         15      /* Software termination signal from kill

 #define SIGBREAK        21      /* Ctrl-Break sequence */
 #define SIGABRT         22      /* abnormal termination triggered by 
 abort
 call */

 (the above are just the windows signals)

Yes. If you want to see how it's done, see internal\deh.c function _d_translate_se_to_d_exception()
 or the third option, my favourite:
   - The compiler defines a 'char[] file' and 'uint line' which I can

 Why is that so hard to do?

I always thought __FILE__ and __LINE__ are just so ugly.

So change the names. debug.file and debug.line are fine by me.
 But when you think
 about it, __FILE__ and __LINE__ only really make sense for macros. Using
 them directly is pointless, isn't it?

No.
 Wouldn't it make sense to throw a
 string, and then grep on that string if you can't remember where it came
 from?

Then you have to make sure all your strings aren't the same, if the same problem is thrown from several locations "Out of memory" you want the same string.
 You can also simply write:
     assert(p != null);
 and not generate a seg fault.

Yeah, as long as I remember to check for them all time, I'd rather not have to, like you said why do this when the hardware will do it for me ;) Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 29 2004
prev sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
I have written some code that allows you to view & dump the current 
stack.  My thought is that many exception types should, by default, 
perform this stack dump when they are created.  Then, whenever they are 
caught, the user can implement a stack backtrace.

We need to add these functions to class Thread:


class Thread {
...
     static byte[] ViewStack() {
       Thread t = <getCurrentThread>;
       return t.ViewStack();
     }

     byte[] ViewStack() {
       // swap the array indices for STACK_GROWS_UP
       assert(this.stackBottom > Thread.getESP());
       return (cast(byte*)0)[Thread.getESP()..this.stackBottom];
     }
...
}


I created this class.  Some types of exceptions probably should not do a 
stack dump; others (like SegFault and AssertError) should do so. 
Perhaps AssertError should be a child of a class something like this:


class Error_DumpsStack : Error {
   byte[] stackDump;
   this(char[] arg) {
     super(arg);
     stackDump = Thread.ViewStack().dup;
   }
}


Finally, users (or perhaps the standard D loader function?) should do 
this.  This code requires that somebody write a function 
PrintStackBackTrace(byte[]):


int main(char[] args) {
   try {
     ...put your main code here...
   }
   catch(Error_DumpsStack e) {
     PrintStackBacktrace(e.stackDump);
   }
   catch(Error e) {
     ...whatever...
   }
}
Jul 29 2004
next sibling parent Ant <duitoolkit yahoo.ca> writes:
On Thu, 29 Jul 2004 17:51:36 -0700, Russ Lewis wrote:

 I have written some code that allows you to view & dump the current 
 stack.

50 lines of code? What are we waiting? oh, right, it took 1 year to get listdir on linux version. Walter, please, this should have been there since 0.01. Ant
Jul 29 2004
prev sibling next sibling parent reply parabolis <parabolis softhome.net> writes:
Russ Lewis wrote:

 I have written some code that allows you to view & dump the current 
 stack.  My thought is that many exception types should, by default, 

Any chance you will post it?
 perform this stack dump when they are created.  Then, whenever they are 
 caught, the user can implement a stack backtrace.
 
 We need to add these functions to class Thread:

Why not add char[] stackDump to Exception and fill it in in the Exception construction? You should also be able to add a function printStackTrace() to Exception and all Errors will thus have the data by default.
Jul 29 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
parabolis wrote:
 Russ Lewis wrote:
 
 I have written some code that allows you to view & dump the current 
 stack.  My thought is that many exception types should, by default, 

Any chance you will post it?

Well, what I've posted already is the distilled core of the code. I'll post the whole program at the bottom of this message.
 Why not add char[] stackDump to Exception and fill it in in the 
 Exception construction? You should also be able to add a function 
 printStackTrace() to Exception and all Errors will thus have the data by 
 default.

IMHO, you shouldn't have stack trace in every Exception because some are likely to be caught and handled...and then doing a stack trace would be wasteful. Perhaps I am wrong, though. Here's the complete program. It compiles and (seems to) work on linux.
 import std.thread;
 import std.string;
  
 byte[] ViewStack(Thread t) {
   assert(t.stackBottom > Thread.getESP());
   return (cast(byte*)0)[Thread.getESP()..t.stackBottom];
 }
  
 byte[] ViewStack() {
   return ViewStack(Thread.getAll()[0]);
 }
  
 class Error_DumpsStack : Error {
   byte[] stackDump;
   this(char[] arg) {
     super(arg);
     stackDump = ViewStack().dup;
   }
 }
  
 int main() {
   byte[] stackView = ViewStack();
   byte[] stackCopy = stackView.dup;
   try {
     throw new Error_DumpsStack("test");
   }
   catch(Error_DumpsStack e) {
     printf("stackView = %d/%p stackCopy=%d/%p stackDump=%d,%p\n",
stackView,stackCopy,e.stackDump);
     printf("cmp(...) = %d\n",
cmp(cast(char[])stackView[stackView.length/2..stackView.length],cast(char[])e.stackDump[e.stackDump.length-stackView.length/2..e.stackDump.length]));
   }
  
   return 0;
 }

Jul 29 2004
next sibling parent Regan Heath <regan netwin.co.nz> writes:
On Thu, 29 Jul 2004 21:40:02 -0700, Russ Lewis 
<spamhole-2001-07-16 deming-os.org> wrote:

 parabolis wrote:
 Russ Lewis wrote:

 I have written some code that allows you to view & dump the current 
 stack.  My thought is that many exception types should, by default,

Any chance you will post it?

Well, what I've posted already is the distilled core of the code. I'll post the whole program at the bottom of this message.
 Why not add char[] stackDump to Exception and fill it in in the 
 Exception construction? You should also be able to add a function 
 printStackTrace() to Exception and all Errors will thus have the data 
 by default.	

IMHO, you shouldn't have stack trace in every Exception because some are likely to be caught and handled...and then doing a stack trace would be wasteful. Perhaps I am wrong, though.

I agree. If some sort of compiler support was added for this then the catch could indicate stack information was required eg. try { } catch(Exception e, Stack[] s) { } or something similar.
 Here's the complete program.  It compiles and (seems to) work on linux.

 import std.thread;
 import std.string;
  byte[] ViewStack(Thread t) {
   assert(t.stackBottom > Thread.getESP());
   return (cast(byte*)0)[Thread.getESP()..t.stackBottom];
 }
  byte[] ViewStack() {
   return ViewStack(Thread.getAll()[0]);
 }
  class Error_DumpsStack : Error {
   byte[] stackDump;
   this(char[] arg) {
     super(arg);
     stackDump = ViewStack().dup;
   }
 }
  int main() {
   byte[] stackView = ViewStack();
   byte[] stackCopy = stackView.dup;
   try {
     throw new Error_DumpsStack("test");
   }
   catch(Error_DumpsStack e) {
     printf("stackView = %d/%p stackCopy=%d/%p stackDump=%d,%p\n", 
 stackView,stackCopy,e.stackDump);
     printf("cmp(...) = %d\n", 
 cmp(cast(char[])stackView[stackView.length/2..stackView.length],cast(char[])e.stackDump[e.stackDump.length-stackView.length/2..e.stackDump.length]));
   }
    return 0;
 }


-- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 29 2004
prev sibling parent reply parabolis <parabolis softhome.net> writes:
Russ Lewis wrote:
 Why not add char[] stackDump to Exception and fill it in in the 
 Exception construction? You should also be able to add a function 
 printStackTrace() to Exception and all Errors will thus have the data 
 by default.

IMHO, you shouldn't have stack trace in every Exception because some are likely to be caught and handled...and then doing a stack trace would be wasteful. Perhaps I am wrong, though.

I can see your point. I am just not sure how one would catch an error thrown by, for example, source you do not control. Say std.utf's validate(). Also I was (and still do) think it might be possible without actually copying the entire stack itself. Provided that Exceptions chaining back through a program do not change ESP then it would be possible for my suggested printStackTrace() to simply start walking from ESP back to main(). However if ESP is changed while Exceptions propagate back then then your current implementation fails to display the part of the stack that actually caused the Error/Exception to be thrown. The only way to fix that is copy the entire stack for each new Error/Exception.
Jul 30 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
parabolis wrote:
 I can see your point. I am just not sure how one would catch an error 
 thrown by, for example, source you do not control. Say std.utf's 
 validate().
 
 Also I was (and still do) think it might be possible without actually 
 copying the entire stack itself. Provided that Exceptions chaining back 
 through a program do not change ESP then it would be possible for my 
 suggested printStackTrace() to simply start walking from ESP back to 
 main().
 
 However if ESP is changed while Exceptions propagate back then then your 
 current implementation fails to display the part of the stack that 
 actually caused the Error/Exception to be thrown. The only way to fix 
 that is copy the entire stack for each new Error/Exception.

I'm pretty sure that 'esp' is the name of the Stack Pointer register in i386. So yeah, getESP() will probably change as soon as you leave your current stack frame. Come to think of it, I've seen it change when I just declare another local variable: uint a = Thread.getESP(); uint b = Thread.getESP(); // b will be a+4, IIRC Plus, when you go back many stack frames to a catch() block and then run code there, you are overwriting old stack frames and a backtrace is impossible. I'm starting to think that the "stackDump" variable should be added to Error itself, but only some types of Error would initialize it by default. But you could add a stack dump at any time if you wanted: try { CallLibraryFunction(); } catch(Error e) { if(e.stackDump.length == 0) e.DumpStack(); }
Jul 30 2004
parent reply parabolis <parabolis softhome.net> writes:
Russ Lewis wrote:
 
 I'm pretty sure that 'esp' is the name of the Stack Pointer register in 
 i386.  So yeah, getESP() will probably change as soon as you leave your 
 current stack frame.

Yes ESP is the stack pointer and leaving the 'stack frame' means that you either called a function (call ???) or returned from a function (ret). The call instruction pushes the current IP onto the stack and ret results in a previously pushed IP being popped so ESP is pretty much guaranteed to change. However from what I have seen D's 'stack frame' is nothing more than just the IP that is pushed during a call instruction. (I am no expert an hope any real experts will point out any errors/over simplifications I made)
 
 Come to think of it, I've seen it change when I just declare another 
 local variable:
 
   uint a = Thread.getESP();
   uint b = Thread.getESP();
     // b will be a+4, IIRC

Usually local variables are stored on the stack and so allocating variables is done by moving ESP to account for the amount of variable you just allocated. (Again - I am no expert an hope any real experts will point out any errors/over simplifications I made)
 
 Plus, when you go back many stack frames to a catch() block and then run 
 code there, you are overwriting old stack frames and a backtrace is 
 impossible.

I am not yet familiar enough with D's internals to know how the try/catch/finally semantics are actually implemented.
 
 I'm starting to think that the "stackDump" variable should be added to 
 Error itself, but only some types of Error would initialize it by 
 default.  But you could add a stack dump at any time if you wanted:
 
   try {
     CallLibraryFunction();
   }
   catch(Error e) {
     if(e.stackDump.length == 0)
       e.DumpStack();
   }
 

I was writing a (rather long) post to explain why I still think my suggestion of adding a printStackTrace() function to Exception (instead of Error as Error inherits from Exception) is what you want to do. I would implement such a function by assuming the try/catch/finally implementation does not write to the stack on the way back towards main. If this is true then all you really need to know to implement printStackTrace() is what the value of ESP was when the Error/Exception was thrown. Then if a used wants a stack trace the printStackTrace() function will provide it. It has only a cost of 4 bytes additional memory for Exception/Error classes and it is a simple mov instruction to initialise it in the constructor. So this method essentially takes absolutely no time or memory to implement. Again any oversights or suggestions are requested.
Jul 30 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
parabolis wrote:
 Russ Lewis wrote:
 
 I'm pretty sure that 'esp' is the name of the Stack Pointer register 
 in i386.  So yeah, getESP() will probably change as soon as you leave 
 your current stack frame.

Yes ESP is the stack pointer and leaving the 'stack frame' means that you either called a function (call ???) or returned from a function (ret). The call instruction pushes the current IP onto the stack and ret results in a previously pushed IP being popped so ESP is pretty much guaranteed to change. However from what I have seen D's 'stack frame' is nothing more than just the IP that is pushed during a call instruction. (I am no expert an hope any real experts will point out any errors/over simplifications I made)
 Come to think of it, I've seen it change when I just declare another 
 local variable:

   uint a = Thread.getESP();
   uint b = Thread.getESP();
     // b will be a+4, IIRC

Usually local variables are stored on the stack and so allocating variables is done by moving ESP to account for the amount of variable you just allocated. (Again - I am no expert an hope any real experts will point out any errors/over simplifications I made)
 Plus, when you go back many stack frames to a catch() block and then 
 run code there, you are overwriting old stack frames and a backtrace 
 is impossible.

I am not yet familiar enough with D's internals to know how the try/catch/finally semantics are actually implemented.
 I'm starting to think that the "stackDump" variable should be added to 
 Error itself, but only some types of Error would initialize it by 
 default.  But you could add a stack dump at any time if you wanted:

   try {
     CallLibraryFunction();
   }
   catch(Error e) {
     if(e.stackDump.length == 0)
       e.DumpStack();
   }

I was writing a (rather long) post to explain why I still think my suggestion of adding a printStackTrace() function to Exception (instead of Error as Error inherits from Exception) is what you want to do. I would implement such a function by assuming the try/catch/finally implementation does not write to the stack on the way back towards main. If this is true then all you really need to know to implement printStackTrace() is what the value of ESP was when the Error/Exception was thrown. Then if a used wants a stack trace the printStackTrace() function will provide it. It has only a cost of 4 bytes additional memory for Exception/Error classes and it is a simple mov instruction to initialise it in the constructor. So this method essentially takes absolutely no time or memory to implement. Again any oversights or suggestions are requested.

As soon as your catch block calls printStackTrace(), you are creating a new stack frame that overwrites the old call stack. The moment you call printStackTrace, it becomes impossible to do a stack backtrace from the old ESP.
Jul 30 2004
parent reply parabolis <parabolis softhome.net> writes:
Russ Lewis wrote:

 
 As soon as your catch block calls printStackTrace(), you are creating a 
 new stack frame that overwrites the old call stack.  The moment you call 
 printStackTrace, it becomes impossible to do a stack backtrace from the 
 old ESP.
 

I am assuming the ESP is already corrupt by the time you would call printStackTrace(). Thus: ================================================================ class Exception { uint esp_cache; this( char[] msg ) { asm { mov esp_cache, ESP; } esp_cache -= 2; // see note below // ... } void printStackTrace() { /* .... */ } class Error : Exception { } ================================================================ The "esp_cache -= 2" is assuming ESP reflects a value 8 bytes above the ESP before this object was created. The first is the return address and the second is the pointer passed to this(). Actually the esp_cache adjustment is more involved than just adjusting for the ESP change resulting from calling the constructor. You are going to have to walk back down the stack when subclasses are created and their constructors alter ESP before the Exception class constructor is called. The code is just to illustrate roughly how the ESP is saved and how it would be accessed in printStackTrace(). Of course you will get strange results if you keep a reference to an Exception class and start using the stack again and then finally call printStackTrace(). Also you get strange results if you just create a new Exception to have it around and then call printStackTrace(). However it may be possible to change things around to alleviate the odd cases.
Jul 30 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
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.
Jul 30 2004
parent reply parabolis <parabolis softhome.net> writes:
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.
Jul 30 2004
next sibling parent reply Regan Heath <regan netwin.co.nz> writes:
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/
Jul 31 2004
parent parabolis <parabolis softhome.net> writes:
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..
 }
 

Aug 01 2004
prev sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
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.
Aug 01 2004
parent parabolis <parabolis softhome.net> writes:
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!
Aug 01 2004
prev sibling parent reply parabolis <parabolis softhome.net> writes:
Russ Lewis wrote:
 
 We need to add these functions to class Thread:
 
 
 class Thread {
 ....
     static byte[] ViewStack() {
       Thread t = <getCurrentThread>;
       return t.ViewStack();
     }
 
     byte[] ViewStack() {
       // swap the array indices for STACK_GROWS_UP
       assert(this.stackBottom > Thread.getESP());
       return (cast(byte*)0)[Thread.getESP()..this.stackBottom];
     }
 ....
 }

That is pretty slick code... Treating all of memory as an array, slicing it and then duplicating. :) However I do not understand why you need a reference to the current thread (or to change std.thread.d) to do this. Shouldn't you be able to get the current thread's ESP just by using: ================================================================ byte[] ViewStack() { uint stack_top; asm { mov stack_top, ESP; } return (cast(byte*)0)[stack_top..this.stackBottom] } ================================================================
Jul 30 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
parabolis wrote:
 That is pretty slick code... Treating all of memory as an array, slicing 
 it and then duplicating. :)

I was pretty proud of it when I stumbled upon it :)
 However I do not understand why you need a reference to the current 
 thread (or to change std.thread.d) to do this. Shouldn't you be able to 
 get the current thread's ESP just by using:
 ================================================================
     byte[] ViewStack() {
         uint stack_top;
         asm { mov stack_top, ESP; }
         return (cast(byte*)0)[stack_top..this.stackBottom]
     }
 ================================================================

When you dump your own stack, your code is probably far better. But I have never taken the time to learn asm, so I use library functions instead. Well, I do think that you should be able to dump the stack from other threads. For instance, if you are about to die, you might want to do this: Thread.pauseAll(); foreach(Thread t; Thread.getAll()) { printf("Stack backtrace of thread %p:\n", t); PrintStackBacktrace(t.ViewStack()); } kill_program();
Jul 30 2004
parent parabolis <parabolis softhome.net> writes:
Russ Lewis wrote:

 
 When you dump your own stack, your code is probably far better.  But I 
 have never taken the time to learn asm, so I use library functions instead.

Yeah I kind of suspected you might not know asm. That is why I sent the code. Now your code can be fast too. :)
 
 Well, I do think that you should be able to dump the stack from other 
 threads.  For instance, if you are about to die, you might want to do this:
 
   Thread.pauseAll();
   foreach(Thread t; Thread.getAll()) {
     printf("Stack backtrace of thread %p:\n", t);
     PrintStackBacktrace(t.ViewStack());
   }
   kill_program();
 

Sute I can see why that might be useful. You should be /absolutely/ sure nothing you do after Threa.pauseAll that results in a synchronised block being excuted however.
Jul 30 2004