www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - GktD: exceptions in handlers cause segfaults.

reply Marco Leise <Marco.Leise gmx.de> writes:
I am trying to throw exceptions in gtk signal handlers, but I
am greeted with segfaults. What's the cause and are there
solutions?
(DMD 2.063.2 on Linux x86-64)

Here is some reduced code:

import gtk.Main;
import gtk.MainWindow;
import gdk.Event;
import gtk.Widget;

class TestWindow : MainWindow
{
	this(string a)
	{
		super(a);
		this.addOnButtonPress(&click);
	}

	bool click(Event, Widget)
	{
		throw new Exception("");
	}
}

int main(string[] args)
{
	try
	{
		Main.init(args);
		auto win = new TestWindow("bla");
		win.showAll();
		Main.run();
		return 0;
	}
	catch (Exception e)
	{
		writeln (e.msg);
		return 1;
	}
}

In gdb I get this (line numbers not matching with above
example):

Program received signal SIGSEGV, Segmentation fault.
0x00000000010f0b60 in rt.deh2.terminate() ()
(gdb) backtrace
#0  0x00000000010f0b60 in rt.deh2.terminate() ()
#1  0x00000000010d538d in _d_throwc ()
#2  0x00000000010bdbcf in main.TestWindow.click() (this=0x7ffff7f25800,
_param_1=0x7ffff7f25800, _param_0=0x7ffff7f23c80) at main.d:589
#3  0x0000000000e4dca3 in gtk.Widget.Widget.callBackButtonPress()
(widgetStruct=0x16b9190, event=0x17dd5d0, _widget=0x7ffff7f25800) at
gtk/Widget.d:693
#4  0x0000003f41385178 in ?? () from /usr/lib64/libgtk-3.so.0
#5  0x0000003ebce114f2 in g_closure_invoke () from
/usr/lib64/libgobject-2.0.so.0
#6  0x0000003ebce22501 in ?? () from /usr/lib64/libgobject-2.0.so.0
#7  0x0000003ebce2ab4e in g_signal_emit_valist () from
/usr/lib64/libgobject-2.0.so.0
#8  0x0000003ebce2b002 in g_signal_emit () from /usr/lib64/libgobject-2.0.so.0
#9  0x0000003f414b299f in ?? () from /usr/lib64/libgtk-3.so.0
#10 0x0000003f4138314b in ?? () from /usr/lib64/libgtk-3.so.0
#11 0x0000003f41384e01 in gtk_main_do_event () from /usr/lib64/libgtk-3.so.0
#12 0x0000003f40a4b372 in ?? () from /usr/lib64/libgdk-3.so.0
#13 0x0000003ebca4dbd3 in g_main_context_dispatch () from
/usr/lib64/libglib-2.0.so.0
#14 0x0000003ebca4ded8 in ?? () from /usr/lib64/libglib-2.0.so.0
#15 0x0000003ebca4e3ba in g_main_loop_run () from /usr/lib64/libglib-2.0.so.0
#16 0x0000003f413841bd in gtk_main () from /usr/lib64/libgtk-3.so.0
#17 0x0000000000d00747 in gtk.Main.Main.run() () at gtk/Main.d:366
#18 0x00000000010bbf3d in D main (args=...) at main.d:60
#19 0x00000000010d6038 in rt.dmain2._d_run_main() ()
#20 0x00000000010d5b6a in rt.dmain2._d_run_main() ()
#21 0x00000000010d6088 in rt.dmain2._d_run_main() ()
#22 0x00000000010d5b6a in rt.dmain2._d_run_main() ()
#23 0x00000000010d5b26 in _d_run_main ()
#24 0x00000000010d5973 in main ()
(gdb) disassemble
Dump of assembler code for function _D2rt4deh29terminateFZv:
   0x00000000010f0b5c <+0>:	push   %rbp
   0x00000000010f0b5d <+1>:	mov    %rsp,%rbp
=> 0x00000000010f0b60 <+4>:	hlt    
   0x00000000010f0b61 <+5>:	pop    %rbp
   0x00000000010f0b62 <+6>:	retq   
End of assembler dump.

-- 
Marco
Jul 19 2013
parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Fri, 19 Jul 2013 11:10:04 +0200
schrieb Marco Leise <Marco.Leise gmx.de>:

dav1d gave me advice on rebuilding druntime with debug symbols.
That lead me to this "GitHub" stack trace:

https://github.com/D-Programming-Language/druntime/blob/v2.063.2/src/rt/deh2.d#L104
https://github.com/D-Programming-Language/druntime/blob/v2.063.2/src/rt/deh2.d#L201
https://github.com/D-Programming-Language/druntime/blob/v2.063.2/src/rt/deh2.d#L246
... back into my code where I throw the exception.

-- 
Marco
Jul 19 2013
parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Fri, 19 Jul 2013 11:47:41 +0200
schrieb Marco Leise <Marco.Leise gmx.de>:

 Am Fri, 19 Jul 2013 11:10:04 +0200
 schrieb Marco Leise <Marco.Leise gmx.de>:
 
 dav1d gave me advice on rebuilding druntime with debug symbols.
 That lead me to this "GitHub" stack trace:
 
 https://github.com/D-Programming-Language/druntime/blob/v2.063.2/src/rt/deh2.d#L104
 https://github.com/D-Programming-Language/druntime/blob/v2.063.2/src/rt/deh2.d#L201
 https://github.com/D-Programming-Language/druntime/blob/v2.063.2/src/rt/deh2.d#L246
 ... back into my code where I throw the exception.
dav1d also told me to watch the DConf 2013 presentation of Higgs where a similar problem with asserts in JIT generated code occurred. It turns out that what Walter explained is the key here, too. All my libraries are compiled without frame pointers, so the simple stack unwinding that D uses fails there. I recompiled glib and gtk+ with -fno-omit-frame-pointer specifically and from now on exception handling works again. Additional info on stack unwinding in the absence of a frame pointer: http://www.yosefk.com/blog/getting-the-call-stack-without-a-frame-pointer.html -- Marco
Jul 19 2013
parent reply Johannes Pfau <nospam example.com> writes:
Am Fri, 19 Jul 2013 12:38:45 +0200
schrieb Marco Leise <Marco.Leise gmx.de>:

 It turns out that what Walter explained is the
 key here, too. All my libraries are compiled without frame
 pointers, so the simple stack unwinding that D uses fails
 there. I recompiled glib and gtk+ with -fno-omit-frame-pointer
 specifically and from now on exception handling works again.
Would be nice to know if this is working with gdc or ldc. In theory it should work as we use gcc's exception handling/stack unwinding so it's probably a dmd bug. Slightly off topic, but maybe interesting: Things can get really nasty if the GCC backend somehow decided a function is nothrow and you throw from there. Unless GTK uses some special __nothrow__ attributes that shouldn't happen in C, but I've seen D code that throws from nothrow functions and at least on ARM that crashes... Might be a gdc bug or a problem with the D language specification.
Jul 19 2013
parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Fri, 19 Jul 2013 21:43:38 +0200
schrieb Johannes Pfau <nospam example.com>:

 Am Fri, 19 Jul 2013 12:38:45 +0200
 schrieb Marco Leise <Marco.Leise gmx.de>:
 
 Would be nice to know if this is working with gdc or ldc. In theory it
 should work as we use gcc's exception handling/stack unwinding so it's
 probably a dmd bug.
That's good to know. Is it possible to port that code into DMD ? How many C libraries do you think are prepared for exceptions ? They might have to clean up memory or system resources.
 Slightly off topic, but maybe interesting:
 Things can get really nasty if the GCC backend somehow decided
 a function is nothrow and you throw from there. Unless GTK uses some
 special __nothrow__ attributes that shouldn't happen in C, but I've seen
 D code that throws from  nothrow functions and at least on ARM that
 crashes... Might be a gdc bug or a problem with the D language
 specification.
If with nothrow you mean the nothrow keyword, it has no value for compiler backends, because assert errors can be thrown anyways. But I probably misunderstood you. -- Marco
Jul 22 2013
parent Johannes Pfau <nospam example.com> writes:
Am Mon, 22 Jul 2013 19:28:10 +0200
schrieb Marco Leise <Marco.Leise gmx.de>:

 Am Fri, 19 Jul 2013 21:43:38 +0200
 schrieb Johannes Pfau <nospam example.com>:
 
 Am Fri, 19 Jul 2013 12:38:45 +0200
 schrieb Marco Leise <Marco.Leise gmx.de>:
 
 Would be nice to know if this is working with gdc or ldc. In theory
 it should work as we use gcc's exception handling/stack unwinding
 so it's probably a dmd bug.
That's good to know. Is it possible to port that code into DMD ?
Probably not. It's implemented in libgcc and I don't know if it's tied to the compiler code gen. Porting is probably not possible because of the license. GCC's exception handling also has its own problems: For example we don't implement exception chaining right now. I just had another look at this and my last statement was not 100% accurate. GCC uses unwind tables instead of frame pointers so it can indeed work without frame pointers. But for C unwind tables are not generated by default unless -fexceptions is used. Interestingly enough it always worked for me ;-)
 How many C libraries do you think are prepared for
 exceptions ? They might have to clean up memory or system
 resources.
Probably very few. I was only talking from the compiler/codegen point of view but any C library that doesn't explicitly allow throwing exceptions probably won't work.
 
 
 If with  nothrow you mean the nothrow keyword, it has no value
 for compiler backends, because assert errors can be thrown
 anyways. But I probably misunderstood you.
Yeah well that's the general perception in the D community. But we need an authoritative statement here. According to TDPL, chapter 9.4 page 307: "Essentially Throwable is considered unrecoverable, so the compiler is relieved of the responsibility of "thinking" of what should happen in case of an exception and consequently _optimizes code under the assumption that nothing is thrown_." That's probably why Iain implemented it so that we directly map nothrow to gcc attribute(__nothrow__). However, I think this statement is supposed to talk about skipping destructors not necessarily unwinding. I don't really have a strong opinion here but according to Walter the backend can optimize quite a lot if it knows for sure that a function can't throw. As asserts can be optimized away anyway it might be useful to rewrite throw(Error) statements in nothrow functions to simply dump a stack trace and message to the console instead of proper exception handling. But as I said I don't have a string opinion here.
Jul 23 2013