Mysterious crash in NSRunLoop, using libobjc2 on Linux

classic Classic list List threaded Threaded
28 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Mysterious crash in NSRunLoop, using libobjc2 on Linux

Lobron, David
Hello GNUstep,

I'm trying to build a large Objective-C and Objective-C++ codebase on Linux using clang, rather than gcc.  As part of this, I'm switching to libobjc2.

Almost all of my code is running correctly, but I'm finding that one unit test is failing when the program receives a SIGABRT.  I ran the program under Valgrind to check for memory corruption, and none was found.  This code all runs correctly when built with gcc and using the gcc runtime, so the error appears to be specific to clang and libobjc2 (although of course it might be a bug in my code that the clang/libobjc2 combination is revealing).

The signal is delivered while the program is reading a fairly large file from disk into a tree data structure.  I set a breakpoint on "abort", and the stack trace pointed to NSRunLoop:

#0  __GI_abort () at abort.c:51
#1  0x00007ffff4f6e521 in _Unwind_Resume (exc=0xa6de460) at ../../../src/libgcc/unwind.inc:234
#2  0x00007ffff5d32625 in -[NSRunLoop limitDateForMode:] (self=0xe13700, _cmd=0x7ffff61b1e28 <.objc_selector_list+368>, mode=0x7ffff61b0e38 <.objc_str>)
    at NSRunLoop.m:1119
#3  0x00007ffff5d32cf6 in -[NSRunLoop runMode:beforeDate:] (self=0xe13700, _cmd=0x7ffff61b1d68 <.objc_selector_list+176>, mode=0x7ffff61b0e38 <.objc_str>,
    date=0xf4d420) at NSRunLoop.m:1285
#4  0x00007ffff5d32e01 in -[NSRunLoop runUntilDate:] (self=0xe13700, _cmd=<optimized out>, date=0xf4d420) at NSRunLoop.m:1334
#5  0x00007ffff6e9d2ec in -[AkamaiDaemon mainLoop] (self=0xb65610, _cmd=<optimized out>) at AkamaiDaemon.m:1080

The line in frame 2 is a NS_ENDHANDLER call, inside of a method called  "- (NSDate*) limitDateForMode: (NSString*)mode".  There's no call to abort in that code.  I tried stepping line by line, but I did not see any exception being thrown here.

Has anyone seen this behavior before?  Should I be checking for stack corruption?  The error is reproducible, so I'm pretty sure it's not random corruption.

Thank you,

David
_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

David Chisnall
On 5 Dec 2017, at 20:01, Lobron, David <[hidden email]> wrote:

>
> Hello GNUstep,
>
> I'm trying to build a large Objective-C and Objective-C++ codebase on Linux using clang, rather than gcc.  As part of this, I'm switching to libobjc2.
>
> Almost all of my code is running correctly, but I'm finding that one unit test is failing when the program receives a SIGABRT.  I ran the program under Valgrind to check for memory corruption, and none was found.  This code all runs correctly when built with gcc and using the gcc runtime, so the error appears to be specific to clang and libobjc2 (although of course it might be a bug in my code that the clang/libobjc2 combination is revealing).
>
> The signal is delivered while the program is reading a fairly large file from disk into a tree data structure.  I set a breakpoint on "abort", and the stack trace pointed to NSRunLoop:
>
> #0  __GI_abort () at abort.c:51
> #1  0x00007ffff4f6e521 in _Unwind_Resume (exc=0xa6de460) at ../../../src/libgcc/unwind.inc:234
> #2  0x00007ffff5d32625 in -[NSRunLoop limitDateForMode:] (self=0xe13700, _cmd=0x7ffff61b1e28 <.objc_selector_list+368>, mode=0x7ffff61b0e38 <.objc_str>)
>    at NSRunLoop.m:1119
> #3  0x00007ffff5d32cf6 in -[NSRunLoop runMode:beforeDate:] (self=0xe13700, _cmd=0x7ffff61b1d68 <.objc_selector_list+176>, mode=0x7ffff61b0e38 <.objc_str>,
>    date=0xf4d420) at NSRunLoop.m:1285
> #4  0x00007ffff5d32e01 in -[NSRunLoop runUntilDate:] (self=0xe13700, _cmd=<optimized out>, date=0xf4d420) at NSRunLoop.m:1334
> #5  0x00007ffff6e9d2ec in -[AkamaiDaemon mainLoop] (self=0xb65610, _cmd=<optimized out>) at AkamaiDaemon.m:1080
>
> The line in frame 2 is a NS_ENDHANDLER call, inside of a method called  "- (NSDate*) limitDateForMode: (NSString*)mode".  There's no call to abort in that code.  I tried stepping line by line, but I did not see any exception being thrown here.

_Unwind_Resume is the function that is used to resume stack unwinding after a cleanup.  It should only be called in Objective-C code at the end of an @finally block (with no corresponding catch block, just @try { … @throw … } @finally {}) or as the result of using __attribute__((cleanup)).

I believe that if you are targetting the gnustep runtime prior to 1.7 (or using a very old version of clang), it will be emitted by clang, but I don’t remember precisely what the EH ABI looked like before I fixed it (I think it called objc_exception_throw(), not _Unwind_Resume).

Please can you use a debug build of libobjc2 (that should prevent tail-call optimisation) and stick a breakpoint on _Unwind_Resume and see where it’s called from?

David


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

Lobron, David

>> The line in frame 2 is a NS_ENDHANDLER call, inside of a method called  "- (NSDate*) limitDateForMode: (NSString*)mode".  There's no call to abort in that code.  I tried stepping line by line, but I did not see any exception being thrown here.
>
> _Unwind_Resume is the function that is used to resume stack unwinding after a cleanup.  It should only be called in Objective-C code at the end of an @finally block (with no corresponding catch block, just @try { … @throw … } @finally {}) or as the result of using __attribute__((cleanup)).
>
> I believe that if you are targetting the gnustep runtime prior to 1.7 (or using a very old version of clang), it will be emitted by clang, but I don’t remember precisely what the EH ABI looked like before I fixed it (I think it called objc_exception_throw(), not _Unwind_Resume).

I'm targeting the gnustep-1.8 in my gnustep-make flags, and all of my own code is being built with -fobjc-runtime=gnustep-1.8.  However, I now see that a few .o files in libobjc2 are being compiled with -fobjc-runtime=gnustep-1.7 rather than 1.8.  Could that be the problem?  I've copied those compile commands at the end of this message.  I'm using clang-6.0.0, from llvm-5.0.0, which should be the latest stable version.

> Please can you use a debug build of libobjc2 (that should prevent tail-call optimisation) and stick a breakpoint on _Unwind_Resume and see where it’s called from?

Done.  The last few lines of the backtrace look like this:

#0  _Unwind_Resume (exc=0xa6c82e0) at ../../../src/libgcc/unwind.inc:224
#1  0x00007ffff6f2851d in -[RadixTree initWithFileDescriptor:symbolParserFunction:context:] (self=<optimized out>, _cmd=<optimized out>, fd=<optimized out>,
    parser=0x4d2430 <parsePingTreeSymbol(NSString*, void*)>, context=0x2dee800) at RadixTree.mm:863
#2  0x00000000004d23e7 in -[PingTreeParser pingTreeWithFile:] (self=0x7fffffffb998, _cmd=<optimized out>, path=0x0) at PingState.mm:305

RadixTree.mm is a file from my own codebase.  Line 863 of that code is an NS_ENDHANDLER block.  The lines leading up to, and including, line 863 look like this:

        readTree(self, _root, indexMap);
        gzclose(_gzFile);
        _gzFile = 0;
    } NS_HANDLER {
        [self release];
        [localException retain];
        [pool release];
        [[localException autorelease] raise];
    } NS_ENDHANDLER

I can send a longer excerpt if it would be helpful.  The code itself has been around a long time, and has worked under gcc/gnu runtime for a long time.  Also, it's running fine with clang/libobjc2 on a number of similar inputs in other unit tests- it's just this one particular test that triggers the SIGABORT.  

Is there another debugging step I might take here, or does this output suggest the source of the problem?

Thank you,

David

Possibly problematic compile lines that target gnustep-1.7:

/home/dlobron/build/clangport/akamai/llvm/llvm-5.0.0.install/bin/clang -DGC_DEBUG -DGNUSTEP -DNO_LEGACY -DTYPE_DEPENDENT_DISPATCH -D_BSD_SOURCE=1 -D_XOPEN_SOURCE=700 -D__BSD_VISIBLE=1 -D__OBJC_RUNTIME_INTERNAL__=1 -Dobjc_EXPORTS  -std=gnu99  -fexceptions -g -O0 -fno-inline -g -fPIC    -Wno-deprecated-objc-isa-usage -Wno-objc-root-class -fobjc-runtime=gnustep-1.7 -o CMakeFiles/objc.dir/Protocol2.m.o   -c /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/Protocol2.m
[ 82%] Building ASM object CMakeFiles/objc.dir/objc_msgSend.S.o
/usr/bin/clang -DGC_DEBUG -DGNUSTEP -DNO_LEGACY -DTYPE_DEPENDENT_DISPATCH -D_BSD_SOURCE=1 -D_XOPEN_SOURCE=700 -D__BSD_VISIBLE=1 -D__OBJC_RUNTIME_INTERNAL__=1 -Dobjc_EXPORTS  -c   -o CMakeFiles/objc.dir/objc_msgSend.S.o -c /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/objc_msgSend.S
[ 85%] Building C object CMakeFiles/objc.dir/arc.m.o
/home/dlobron/build/clangport/akamai/llvm/llvm-5.0.0.install/bin/clang -DGC_DEBUG -DGNUSTEP -DNO_LEGACY -DTYPE_DEPENDENT_DISPATCH -D_BSD_SOURCE=1 -D_XOPEN_SOURCE=700 -D__BSD_VISIBLE=1 -D__OBJC_RUNTIME_INTERNAL__=1 -Dobjc_EXPORTS  -std=gnu99  -fexceptions -g -O0 -fno-inline -g -fPIC    -Wno-deprecated-objc-isa-usage -Wno-objc-root-class -fobjc-runtime=gnustep-1.7 -o CMakeFiles/objc.dir/arc.m.o   -c /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/arc.m
[ 88%] Building C object CMakeFiles/objc.dir/associate.m.o
/home/dlobron/build/clangport/akamai/llvm/llvm-5.0.0.install/bin/clang -DGC_DEBUG -DGNUSTEP -DNO_LEGACY -DTYPE_DEPENDENT_DISPATCH -D_BSD_SOURCE=1 -D_XOPEN_SOURCE=700 -D__BSD_VISIBLE=1 -D__OBJC_RUNTIME_INTERNAL__=1 -Dobjc_EXPORTS  -std=gnu99  -fexceptions -g -O0 -fno-inline -g -fPIC    -Wno-deprecated-objc-isa-usage -Wno-objc-root-class -fobjc-runtime=gnustep-1.7 -o CMakeFiles/objc.dir/associate.m.o   -c /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/associate.m
[ 91%] Building C object CMakeFiles/objc.dir/blocks_runtime.m.o
/home/dlobron/build/clangport/akamai/llvm/llvm-5.0.0.install/bin/clang -DGC_DEBUG -DGNUSTEP -DNO_LEGACY -DTYPE_DEPENDENT_DISPATCH -D_BSD_SOURCE=1 -D_XOPEN_SOURCE=700 -D__BSD_VISIBLE=1 -D__OBJC_RUNTIME_INTERNAL__=1 -Dobjc_EXPORTS  -std=gnu99  -fexceptions -g -O0 -fno-inline -g -fPIC    -Wno-deprecated-objc-isa-usage -Wno-objc-root-class -fobjc-runtime=gnustep-1.7 -o CMakeFiles/objc.dir/blocks_runtime.m.o   -c /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/blocks_runtime.m
[ 94%] Building C object CMakeFiles/objc.dir/properties.m.o
/home/dlobron/build/clangport/akamai/llvm/llvm-5.0.0.install/bin/clang -DGC_DEBUG -DGNUSTEP -DNO_LEGACY -DTYPE_DEPENDENT_DISPATCH -D_BSD_SOURCE=1 -D_XOPEN_SOURCE=700 -D__BSD_VISIBLE=1 -D__OBJC_RUNTIME_INTERNAL__=1 -Dobjc_EXPORTS  -std=gnu99  -fexceptions -g -O0 -fno-inline -g -fPIC    -Wno-deprecated-objc-isa-usage -Wno-objc-root-class -fobjc-runtime=gnustep-1.7 -o CMakeFiles/objc.dir/properties.m.o   -c /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/properties.m
[ 97%] Building C object CMakeFiles/objc.dir/gc_none.c.o
/home/dlobron/build/clangport/akamai/llvm/llvm-5.0.0.install/bin/clang -DGC_DEBUG -DGNUSTEP -DNO_LEGACY -DTYPE_DEPENDENT_DISPATCH -D_BSD_SOURCE=1 -D_XOPEN_SOURCE=700 -D__BSD_VISIBLE=1 -D__OBJC_RUNTIME_INTERNAL__=1 -Dobjc_EXPORTS  -std=gnu99  -fexceptions -g -O0 -fno-inline -g -fPIC    -Wno-deprecated-objc-isa-usage -Wno-objc-root-class -fobjc-runtime=gnustep-1.7 -o CMakeFiles/objc.dir/gc_none.c.o   -c /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/gc_none.c
[100%] Linking C shared library libobjc.so



_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

David Chisnall

> On 6 Dec 2017, at 16:53, Lobron, David <[hidden email]> wrote:
>
>
>>> The line in frame 2 is a NS_ENDHANDLER call, inside of a method called  "- (NSDate*) limitDateForMode: (NSString*)mode".  There's no call to abort in that code.  I tried stepping line by line, but I did not see any exception being thrown here.
>>
>> _Unwind_Resume is the function that is used to resume stack unwinding after a cleanup.  It should only be called in Objective-C code at the end of an @finally block (with no corresponding catch block, just @try { … @throw … } @finally {}) or as the result of using __attribute__((cleanup)).
>>
>> I believe that if you are targetting the gnustep runtime prior to 1.7 (or using a very old version of clang), it will be emitted by clang, but I don’t remember precisely what the EH ABI looked like before I fixed it (I think it called objc_exception_throw(), not _Unwind_Resume).
>
> I'm targeting the gnustep-1.8 in my gnustep-make flags, and all of my own code is being built with -fobjc-runtime=gnustep-1.8.  However, I now see that a few .o files in libobjc2 are being compiled with -fobjc-runtime=gnustep-1.7 rather than 1.8.  Could that be the problem?  I've copied those compile commands at the end of this message.  I'm using clang-6.0.0, from llvm-5.0.0, which should be the latest stable version.
>
>> Please can you use a debug build of libobjc2 (that should prevent tail-call optimisation) and stick a breakpoint on _Unwind_Resume and see where it’s called from?
>
> Done.  The last few lines of the backtrace look like this:
>
> #0  _Unwind_Resume (exc=0xa6c82e0) at ../../../src/libgcc/unwind.inc:224
> #1  0x00007ffff6f2851d in -[RadixTree initWithFileDescriptor:symbolParserFunction:context:] (self=<optimized out>, _cmd=<optimized out>, fd=<optimized out>,
>    parser=0x4d2430 <parsePingTreeSymbol(NSString*, void*)>, context=0x2dee800) at RadixTree.mm:863
> #2  0x00000000004d23e7 in -[PingTreeParser pingTreeWithFile:] (self=0x7fffffffb998, _cmd=<optimized out>, path=0x0) at PingState.mm:305
>
> RadixTree.mm is a file from my own codebase.  Line 863 of that code is an NS_ENDHANDLER block.  The lines leading up to, and including, line 863 look like this:
>
>        readTree(self, _root, indexMap);
>        gzclose(_gzFile);
> _gzFile = 0;
>    } NS_HANDLER {
>        [self release];
>        [localException retain];
>        [pool release];
>        [[localException autorelease] raise];
>    } NS_ENDHANDLER
>
> I can send a longer excerpt if it would be helpful.  The code itself has been around a long time, and has worked under gcc/gnu runtime for a long time.  Also, it's running fine with clang/libobjc2 on a number of similar inputs in other unit tests- it's just this one particular test that triggers the SIGABORT.  
>
> Is there another debugging step I might take here, or does this output suggest the source of the problem?


I’m not sure why you’re seeing a call to _Unwind_Resume from NS_ENDHANDLER.  It looks as if you have some cleanup code in that block that is being run when you throw the exception from the -raise method.  Actually, this entire block looks fragile and redundant - what happens if you simply delete the entire EH block here?  Exception objects are retained / released by the runtime, so don’t need special handling to propagate past autoreleasepool boundaries.  

David


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

Lobron, David

> I’m not sure why you’re seeing a call to _Unwind_Resume from NS_ENDHANDLER.  It looks as if you have some cleanup code in that block that is being run when you throw the exception from the -raise method.  Actually, this entire block looks fragile and redundant - what happens if you simply delete the entire EH block here?  Exception objects are retained / released by the runtime, so don’t need special handling to propagate past autoreleasepool boundaries.  

I did some more sleuthing, and I realized the reason this unit test is special: it has an invalid tree symbol which prompts an exception.  With the gcc build and runtime, that exception is caught, but with clang/libobjc2, the stack unwinds after the exception and then abort is called partway through the unwind.  I removed the NS_HANDLER block from my code, as you suggested above, but that did not appear to have any effect on the behavior.  However, I followed the breakpoints on _Unwind_Resume, and I traced the entire process:

The initial exception is thrown where I would expect, at the point where the tree parser hits the invalid symbol.  The stack at that point is:

#0  -[NSException raise] (self=0x17151c80, _cmd=0x7ffff617a070 <.objc_selector_list+64>) at NSException.m:1137
#1  0x00007ffff5cc5a80 in +[NSException raise:format:] (self=0x17151c80, _cmd=<optimized out>, name=0xa447668, format=0x59d34) at NSException.m:1016
#2  0x00000000004d2765 in parsePingTreeSymbol(NSString*, void*) (symbol=0xa470340, context=0x2e0f690) at PingState.mm:83
#3  0x00007ffff6f28ec4 in -[RadixTree initWithFileDescriptor:symbolParserFunction:context:] (self=0x391e, _cmd=<optimized out>, fd=<optimized out>,
    parser=0x4d2430 <parsePingTreeSymbol(NSString*, void*)>, context=0x2e0f690) at RadixTree.mm:827
#4  0x00000000004d23e7 in -[PingTreeParser pingTreeWithFile:] (self=0x0, _cmd=<optimized out>, path=0xa447668) at PingState.mm:305
#5  0x00007ffff6ecd777 in -[FileAcceptor checkForObject] (self=0x319f220, _cmd=<optimized out>) at FileAcceptor.m:630
#6  0x00000000004ccd1c in PingScoresRebaser::rebase() (this=0x3596570) at PingScoresRebaser.mm:208
#7  0x00000000004c302b in RebaserProxy<PingScoresRebaser>::rebase() (this=<optimized out>) at ./RebaserProxy.h:202
#8  0x00000000004c1cc7 in RebaserProxy<PingScoresRebaser>::operator->() (this=0xfc6db0) at ./RebaserProxy.h:262
#9  0x00000000004bdaaa in -[MapMaker readNewInputs] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:1708
#10 0x00000000004bdbbd in -[MapMaker readInputsAndMakeMap] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:1739
#11 0x00000000004bee29 in -[MapMaker doWork] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:2017
#12 0x00007ffff6e9cd16 in -[AkamaiDaemon(PrivateMethods) doOneCycle:] (self=0xb65e40, _cmd=<optimized out>, ignored=<optimized out>) at AkamaiDaemon.m:467
#13 0x00007ffff5d59837 in -[NSTimer fire] (self=0x1017260, _cmd=<optimized out>) at NSTimer.m:283
#14 0x00007ffff5d34029 in -[NSRunLoop _limitDateForContext:] (self=0xe0e510, _cmd=<optimized out>, context=0xe23600) at NSRunLoop.m:1011
#15 0x00007ffff5d34468 in -[NSRunLoop limitDateForMode:] (self=0xe0e510, _cmd=0x7ffff61b3d18 <.objc_selector_list+96>, mode=0x7ffff61b2e38 <.objc_str>)
    at NSRunLoop.m:1111
#16 0x00007ffff5d34c26 in -[NSRunLoop runMode:beforeDate:] (self=0xe0e510, _cmd=0x7ffff61b3d88 <.objc_selector_list+208>, mode=0x7ffff61b2e38 <.objc_str>,
    date=0xfb2b90) at NSRunLoop.m:1285
#17 0x00007ffff5d34d31 in -[NSRunLoop runUntilDate:] (self=0xe0e510, _cmd=<optimized out>, date=0xfb2b90) at NSRunLoop.m:1334
#18 0x00007ffff6e9f1ec in -[AkamaiDaemon mainLoop] (self=0xb65e40, _cmd=<optimized out>) at AkamaiDaemon.m:1080
#19 0x00000000004c4c09 in main (argc=<optimized out>, argv=<optimized out>) at MapMakerMainFunc.mm:29

When I continue from here, the next breakpoint to hit is the one on _Unwind_Resume:

#0  _Unwind_Resume (exc=0x171607d0) at ../../../src/libgcc/unwind.inc:224
#1  0x00007ffff6f29259 in -[RadixTree initWithFileDescriptor:symbolParserFunction:context:] (self=0x391e, _cmd=<optimized out>, fd=<optimized out>,
    parser=0x4d2430 <parsePingTreeSymbol(NSString*, void*)>, context=0x2e0f690) at /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/ext/new_allocator.h:110
#2  0x00000000004d23e7 in -[PingTreeParser pingTreeWithFile:] (self=0x7fffffffb998, _cmd=<optimized out>, path=0x0) at PingState.mm:305
#3  0x00007ffff6ecd777 in -[FileAcceptor checkForObject] (self=0x319f220, _cmd=<optimized out>) at FileAcceptor.m:630
#4  0x00000000004ccd1c in PingScoresRebaser::rebase() (this=0x3596570) at PingScoresRebaser.mm:208
#5  0x00000000004c302b in RebaserProxy<PingScoresRebaser>::rebase() (this=<optimized out>) at ./RebaserProxy.h:202
#6  0x00000000004c1cc7 in RebaserProxy<PingScoresRebaser>::operator->() (this=0xfc6db0) at ./RebaserProxy.h:262
#7  0x00000000004bdaaa in -[MapMaker readNewInputs] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:1708
#8  0x00000000004bdbbd in -[MapMaker readInputsAndMakeMap] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:1739
#9  0x00000000004bee29 in -[MapMaker doWork] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:2017
#10 0x00007ffff6e9cd16 in -[AkamaiDaemon(PrivateMethods) doOneCycle:] (self=0xb65e40, _cmd=<optimized out>, ignored=<optimized out>) at AkamaiDaemon.m:467
#11 0x00007ffff5d59837 in -[NSTimer fire] (self=0x1017260, _cmd=<optimized out>) at NSTimer.m:283
#12 0x00007ffff5d34029 in -[NSRunLoop _limitDateForContext:] (self=0xe0e510, _cmd=<optimized out>, context=0xe23600) at NSRunLoop.m:1011
#13 0x00007ffff5d34468 in -[NSRunLoop limitDateForMode:] (self=0xe0e510, _cmd=0x7ffff61b3d18 <.objc_selector_list+96>, mode=0x7ffff61b2e38 <.objc_str>)
    at NSRunLoop.m:1111
#14 0x00007ffff5d34c26 in -[NSRunLoop runMode:beforeDate:] (self=0xe0e510, _cmd=0x7ffff61b3d88 <.objc_selector_list+208>, mode=0x7ffff61b2e38 <.objc_str>,
    date=0xfb2b90) at NSRunLoop.m:1285
#15 0x00007ffff5d34d31 in -[NSRunLoop runUntilDate:] (self=0xe0e510, _cmd=<optimized out>, date=0xfb2b90) at NSRunLoop.m:1334
#16 0x00007ffff6e9f1ec in -[AkamaiDaemon mainLoop] (self=0xb65e40, _cmd=<optimized out>) at AkamaiDaemon.m:1080
#17 0x00000000004c4c09 in main (argc=<optimized out>, argv=<optimized out>) at MapMakerMainFunc.mm:29

Continuing, the _Unwind_Resume is hit several more times as the stack unwinds:

#0  _Unwind_Resume (exc=0x171607d0) at ../../../src/libgcc/unwind.inc:224
#1  0x00007ffff6ecd8a0 in -[FileAcceptor checkForObject] (self=0x319f220, _cmd=<optimized out>) at FileAcceptor.m:639
#2  0x00000000004ccd1c in PingScoresRebaser::rebase() (this=0x3596570) at PingScoresRebaser.mm:208
#3  0x00000000004c302b in RebaserProxy<PingScoresRebaser>::rebase() (this=<optimized out>) at ./RebaserProxy.h:202
#4  0x00000000004c1cc7 in RebaserProxy<PingScoresRebaser>::operator->() (this=0xfc6db0) at ./RebaserProxy.h:262
#5  0x00000000004bdaaa in -[MapMaker readNewInputs] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:1708
#6  0x00000000004bdbbd in -[MapMaker readInputsAndMakeMap] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:1739
#7  0x00000000004bee29 in -[MapMaker doWork] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:2017
#8  0x00007ffff6e9cd16 in -[AkamaiDaemon(PrivateMethods) doOneCycle:] (self=0xb65e40, _cmd=<optimized out>, ignored=<optimized out>) at AkamaiDaemon.m:467
#9  0x00007ffff5d59837 in -[NSTimer fire] (self=0x1017260, _cmd=<optimized out>) at NSTimer.m:283
#10 0x00007ffff5d34029 in -[NSRunLoop _limitDateForContext:] (self=0xe0e510, _cmd=<optimized out>, context=0xe23600) at NSRunLoop.m:1011
#11 0x00007ffff5d34468 in -[NSRunLoop limitDateForMode:] (self=0xe0e510, _cmd=0x7ffff61b3d18 <.objc_selector_list+96>, mode=0x7ffff61b2e38 <.objc_str>)
    at NSRunLoop.m:1111
#12 0x00007ffff5d34c26 in -[NSRunLoop runMode:beforeDate:] (self=0xe0e510, _cmd=0x7ffff61b3d88 <.objc_selector_list+208>, mode=0x7ffff61b2e38 <.objc_str>,
    date=0xfb2b90) at NSRunLoop.m:1285
#13 0x00007ffff5d34d31 in -[NSRunLoop runUntilDate:] (self=0xe0e510, _cmd=<optimized out>, date=0xfb2b90) at NSRunLoop.m:1334
#14 0x00007ffff6e9f1ec in -[AkamaiDaemon mainLoop] (self=0xb65e40, _cmd=<optimized out>) at AkamaiDaemon.m:1080
#15 0x00000000004c4c09 in main (argc=<optimized out>, argv=<optimized out>) at MapMakerMainFunc.mm:29


#0  _Unwind_Resume (exc=0x171607d0) at ../../../src/libgcc/unwind.inc:224
#1  0x00000000004bdb96 in -[MapMaker readNewInputs] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:1721
#2  0x00000000004bdbbd in -[MapMaker readInputsAndMakeMap] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:1739
#3  0x00000000004bee29 in -[MapMaker doWork] (self=0xb65e40, _cmd=<optimized out>) at MapMaker.mm:2017
#4  0x00007ffff6e9cd16 in -[AkamaiDaemon(PrivateMethods) doOneCycle:] (self=0xb65e40, _cmd=<optimized out>, ignored=<optimized out>) at AkamaiDaemon.m:467
#5  0x00007ffff5d59837 in -[NSTimer fire] (self=0x1017260, _cmd=<optimized out>) at NSTimer.m:283
#6  0x00007ffff5d34029 in -[NSRunLoop _limitDateForContext:] (self=0xe0e510, _cmd=<optimized out>, context=0xe23600) at NSRunLoop.m:1011
#7  0x00007ffff5d34468 in -[NSRunLoop limitDateForMode:] (self=0xe0e510, _cmd=0x7ffff61b3d18 <.objc_selector_list+96>, mode=0x7ffff61b2e38 <.objc_str>)
    at NSRunLoop.m:1111
#8  0x00007ffff5d34c26 in -[NSRunLoop runMode:beforeDate:] (self=0xe0e510, _cmd=0x7ffff61b3d88 <.objc_selector_list+208>, mode=0x7ffff61b2e38 <.objc_str>,
    date=0xfb2b90) at NSRunLoop.m:1285
#9  0x00007ffff5d34d31 in -[NSRunLoop runUntilDate:] (self=0xe0e510, _cmd=<optimized out>, date=0xfb2b90) at NSRunLoop.m:1334
#10 0x00007ffff6e9f1ec in -[AkamaiDaemon mainLoop] (self=0xb65e40, _cmd=<optimized out>) at AkamaiDaemon.m:1080
#11 0x00000000004c4c09 in main (argc=<optimized out>, argv=<optimized out>) at MapMakerMainFunc.mm:29


#0  _Unwind_Resume (exc=0x171607d0) at ../../../src/libgcc/unwind.inc:224
#1  0x00007ffff6e9ce10 in -[AkamaiDaemon(PrivateMethods) doOneCycle:] (self=0xb65e40, _cmd=<optimized out>, ignored=<optimized out>) at AkamaiDaemon.m:470
#2  0x00007ffff5d59837 in -[NSTimer fire] (self=0x1017260, _cmd=<optimized out>) at NSTimer.m:283
#3  0x00007ffff5d34029 in -[NSRunLoop _limitDateForContext:] (self=0xe0e510, _cmd=<optimized out>, context=0xe23600) at NSRunLoop.m:1011
#4  0x00007ffff5d34468 in -[NSRunLoop limitDateForMode:] (self=0xe0e510, _cmd=0x7ffff61b3d18 <.objc_selector_list+96>, mode=0x7ffff61b2e38 <.objc_str>)
    at NSRunLoop.m:1111
#5  0x00007ffff5d34c26 in -[NSRunLoop runMode:beforeDate:] (self=0xe0e510, _cmd=0x7ffff61b3d88 <.objc_selector_list+208>, mode=0x7ffff61b2e38 <.objc_str>,
    date=0xfb2b90) at NSRunLoop.m:1285
#6  0x00007ffff5d34d31 in -[NSRunLoop runUntilDate:] (self=0xe0e510, _cmd=<optimized out>, date=0xfb2b90) at NSRunLoop.m:1334
#7  0x00007ffff6e9f1ec in -[AkamaiDaemon mainLoop] (self=0xb65e40, _cmd=<optimized out>) at AkamaiDaemon.m:1080
#8  0x00000000004c4c09 in main (argc=<optimized out>, argv=<optimized out>) at MapMakerMainFunc.mm:29


#0  _Unwind_Resume (exc=0x171607d0) at ../../../src/libgcc/unwind.inc:224
#1  0x00007ffff5d599ac in -[NSTimer fire] (self=0x1017260, _cmd=<optimized out>) at NSTimer.m:287
#2  0x00007ffff5d34029 in -[NSRunLoop _limitDateForContext:] (self=0xe0e510, _cmd=<optimized out>, context=0xe23600) at NSRunLoop.m:1011
#3  0x00007ffff5d34468 in -[NSRunLoop limitDateForMode:] (self=0xe0e510, _cmd=0x7ffff61b3d18 <.objc_selector_list+96>, mode=0x7ffff61b2e38 <.objc_str>)
    at NSRunLoop.m:1111
#4  0x00007ffff5d34c26 in -[NSRunLoop runMode:beforeDate:] (self=0xe0e510, _cmd=0x7ffff61b3d88 <.objc_selector_list+208>, mode=0x7ffff61b2e38 <.objc_str>,
    date=0xfb2b90) at NSRunLoop.m:1285
#5  0x00007ffff5d34d31 in -[NSRunLoop runUntilDate:] (self=0xe0e510, _cmd=<optimized out>, date=0xfb2b90) at NSRunLoop.m:1334
#6  0x00007ffff6e9f1ec in -[AkamaiDaemon mainLoop] (self=0xb65e40, _cmd=<optimized out>) at AkamaiDaemon.m:1080
#7  0x00000000004c4c09 in main (argc=<optimized out>, argv=<optimized out>) at MapMakerMainFunc.mm:29


#0  _Unwind_Resume (exc=0x171607d0) at ../../../src/libgcc/unwind.inc:224
#1  0x00007ffff5d34555 in -[NSRunLoop limitDateForMode:] (self=0xe0e510, _cmd=0x7ffff61b3d18 <.objc_selector_list+96>, mode=0x7ffff61b2e38 <.objc_str>)
    at NSRunLoop.m:1119
#2  0x00007ffff5d34c26 in -[NSRunLoop runMode:beforeDate:] (self=0xe0e510, _cmd=0x7ffff61b3d88 <.objc_selector_list+208>, mode=0x7ffff61b2e38 <.objc_str>,
    date=0xfb2b90) at NSRunLoop.m:1285
#3  0x00007ffff5d34d31 in -[NSRunLoop runUntilDate:] (self=0xe0e510, _cmd=<optimized out>, date=0xfb2b90) at NSRunLoop.m:1334
#4  0x00007ffff6e9f1ec in -[AkamaiDaemon mainLoop] (self=0xb65e40, _cmd=<optimized out>) at AkamaiDaemon.m:1080
#5  0x00000000004c4c09 in main (argc=<optimized out>, argv=<optimized out>) at MapMakerMainFunc.mm:29


But then, abort is called when it tries to unwind past limitDateForMode:

#0  __GI_abort () at abort.c:51
#1  0x00007ffff4f70521 in _Unwind_Resume (exc=0x171607d0) at ../../../src/libgcc/unwind.inc:234
#2  0x00007ffff5d34555 in -[NSRunLoop limitDateForMode:] (self=0xe0e510, _cmd=0x7ffff61b3d18 <.objc_selector_list+96>, mode=0x7ffff61b2e38 <.objc_str>)
    at NSRunLoop.m:1119
#3  0x00007ffff5d34c26 in -[NSRunLoop runMode:beforeDate:] (self=0xe0e510, _cmd=0x7ffff61b3d88 <.objc_selector_list+208>, mode=0x7ffff61b2e38 <.objc_str>,
    date=0xfb2b90) at NSRunLoop.m:1285
#4  0x00007ffff5d34d31 in -[NSRunLoop runUntilDate:] (self=0xe0e510, _cmd=<optimized out>, date=0xfb2b90) at NSRunLoop.m:1334
#5  0x00007ffff6e9f1ec in -[AkamaiDaemon mainLoop] (self=0xb65e40, _cmd=<optimized out>) at AkamaiDaemon.m:1080
#6  0x00000000004c4c09 in main (argc=<optimized out>, argv=<optimized out>) at MapMakerMainFunc.mm:29

So the question is: why is abort being called here?  Why doesn't the stack continue to unwind?  I did notice that certain frames seem to be skipped in the unwind sequence above, and the line numbers reported do change a little - could that be related to the problem?  I'm linking with libunwind-1.1, and I'm building it with a fairly standard set of options (copied below).

--David

cd /home/dlobron/build/clangport/akamai/libunwind/libunwind-1.1 && setarch x86_64 ./configure --enable-static --disable-shared --prefix=/home/dlobron/build/clangport/akamai/libunwind/INSTALLED --libdir=/home/dlobron/build/clangport/akamai/common/lib --includedir=/home/dlobron/build/clangport/akamai/common/include/libunwind --disable-cxx-exceptions --disable-minidebuginfo --build=x86_64-pc-linux-gnu --target=x86_64-pc-linux-gnu  CC="/home/dlobron/build/clangport/akamai/llvm/llvm-5.0.0.install/bin/clang" CPP="/home/dlobron/build/clangport/akamai/llvm/llvm-5.0.0.install/bin/clang -E" CXX="/home/dlobron/build/clangport/akamai/llvm/llvm-5.0.0.install/bin/clang++" CFLAGS="-m64 -march=opteron -mno-3dnow -ggdb -O2 -Wall -fPIC -U_FORTIFY_SOURCE -I/home/dlobron/build/clangport/akamai/libunwind" CPPFLAGS="-I/home/dlobron/build/clangport/akamai/common/include -I/usr/local/include" CXXFLAGS="-m64 -march=opteron -mno-3dnow -ggdb -O2 -fPIC" LDFLAGS="-L/home/dlobron/build/clangport/akamai/common/lib -m64 "


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

David Chisnall
On 6 Dec 2017, at 19:04, Lobron, David <[hidden email]> wrote:
>
> So the question is: why is abort being called here?  Why doesn't the stack continue to unwind?  I did notice that certain frames seem to be skipped in the unwind sequence above, and the line numbers reported do change a little - could that be related to the problem?  I'm linking with libunwind-1.1, and I'm building it with a fairly standard set of options (copied below).

When you throw an exception, first the runtime sets up some ObjC-specific things and then calls into the unwind library.  The unwind library then walks up the stack, calling back into the runtime (or another language’s) personality function for each frame to indicate whether it’s found a handler or not.

For each frame, the personality function says one of three things: this one isn’t important, this one contains cleanups, and this one contains a matching catch handler.

If no personality function call finds a catch handler, then the unwinder returns to the runtime, which then calls abort.  That doesn’t seem to be happening here.

If there is a catch handler, then the unwinder walks the stack again, only this time when it finds a catch block the personality function sets two registers (the exception object and the index of the cleanup in a table) and the unwind library returns control to the program.

Once the program has run the cleanup code, it will call _Unwind_Resume and will continue unwinding.  If the unwinder then doesn’t find another cleanup or catch on the stack, then it will call abort, because _Unwind_Resume isn’t expected to return, but it now doesn’t have anywhere to unwind to.

TL;DR: It looks as if the unwinder is first finding a catch block when the exception is thrown, but then not finding it when the exception is rethrown after running cleanups.  This is quite surprising, and the only thing I can think of that would cause it (modulo bugs in the ObjC personality function - which are entirely possible as it’s a horribly complex piece of code) is if the exception passed back into _Unwind_Resume is not the same exception as the one initially thrown.

Please can you also try sticking a breakpoint on objc_exception_throw and objc_exception_rethrow?  There are also a bunch of debug logs that are compiled out in the EH code - if you #define DEBUG_LOG at the top of eh_personality.c then you should see them and get a better idea of what’s going on.

David


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

Lobron, David

> TL;DR: It looks as if the unwinder is first finding a catch block when the exception is thrown, but then not finding it when the exception is rethrown after running cleanups.  This is quite surprising, and the only thing I can think of that would cause it (modulo bugs in the ObjC personality function - which are entirely possible as it’s a horribly complex piece of code) is if the exception passed back into _Unwind_Resume is not the same exception as the one initially thrown.

Thank you for that explanation (it wasn't too long, and I did read it). :-)

> Please can you also try sticking a breakpoint on objc_exception_throw and objc_exception_rethrow?  

I set those breakpoints, in addition to ones on -[NSException raise], _Unwind_Resume, and abort.  I've pasted the steps below (I trimmed the stack traces on all but the first one, for brevity - there are 13 breakpoint hits, including the last one on the abort call):

1) The initial exception:
Breakpoint 1, -[NSException raise] (self=0x1715ac80, _cmd=0x7ffff6179190 <.objc_selector_list+352>) at NSException.m:1137
1137  if (_reserved == 0)
#0  -[NSException raise] (self=0x1715ac80, _cmd=0x7ffff6179190 <.objc_selector_list+352>) at NSException.m:1137
#1  0x00007ffff5cc4af0 in +[NSException raise:format:] (self=0x1715ac80, _cmd=<optimized out>, name=0xa450138, format=0x59d34) at NSException.m:1016
#2  0x00000000004d2745 in parsePingTreeSymbol(NSString*, void*) (symbol=0xa478dd0, context=0x2e17390) at PingState.mm:83
#3  0x00007ffff6f28004 in -[RadixTree initWithFileDescriptor:symbolParserFunction:context:] (self=0x104f940, _cmd=<optimized out>, fd=<optimized out>,
    parser=0x4d2410 <parsePingTreeSymbol(NSString*, void*)>, context=0x2e17390) at RadixTree.mm:827
#4  0x00000000004d23c7 in -[PingTreeParser pingTreeWithFile:] (self=0x0, _cmd=<optimized out>, path=0xa450138) at PingState.mm:305
#5  0x00007ffff6ecc867 in -[FileAcceptor checkForObject] (self=0x2e16db0, _cmd=<optimized out>) at FileAcceptor.m:630
#6  0x00000000004ccd0c in PingScoresRebaser::rebase() (this=0x359e7f0) at PingScoresRebaser.mm:208
#7  0x00000000004c301b in RebaserProxy<PingScoresRebaser>::rebase() (this=<optimized out>) at ./RebaserProxy.h:202
#8  0x00000000004c1cb7 in RebaserProxy<PingScoresRebaser>::operator->() (this=0xf30f60) at ./RebaserProxy.h:262
#9  0x00000000004bda9a in -[MapMaker readNewInputs] (self=0xb645b0, _cmd=<optimized out>) at MapMaker.mm:1708
#10 0x00000000004bdbad in -[MapMaker readInputsAndMakeMap] (self=0xb645b0, _cmd=<optimized out>) at MapMaker.mm:1739
#11 0x00000000004bee19 in -[MapMaker doWork] (self=0xb645b0, _cmd=<optimized out>) at MapMaker.mm:2017
#12 0x00007ffff6e9be16 in -[AkamaiDaemon(PrivateMethods) doOneCycle:] (self=0xb645b0, _cmd=<optimized out>, ignored=<optimized out>) at AkamaiDaemon.m:467
#13 0x00007ffff5d58897 in -[NSTimer fire] (self=0x1037e30, _cmd=<optimized out>) at NSTimer.m:283
#14 0x00007ffff5d33059 in -[NSRunLoop _limitDateForContext:] (self=0xe14760, _cmd=<optimized out>, context=0xe29850) at NSRunLoop.m:1011
#15 0x00007ffff5d33498 in -[NSRunLoop limitDateForMode:] (self=0xe14760, _cmd=0x7ffff61b2d58 <.objc_selector_list+160>, mode=0x7ffff61b1e38 <.objc_str>)
    at NSRunLoop.m:1111
#16 0x00007ffff5d33c56 in -[NSRunLoop runMode:beforeDate:] (self=0xe14760, _cmd=0x7ffff61b2df8 <.objc_selector_list+320>, mode=0x7ffff61b1e38 <.objc_str>,
    date=0xfcaff0) at NSRunLoop.m:1285
#17 0x00007ffff5d33d61 in -[NSRunLoop runUntilDate:] (self=0xe14760, _cmd=<optimized out>, date=0xfcaff0) at NSRunLoop.m:1334
#18 0x00007ffff6e9e2ec in -[AkamaiDaemon mainLoop] (self=0xb645b0, _cmd=<optimized out>) at AkamaiDaemon.m:1080
#19 0x00000000004c4bf9 in main (argc=<optimized out>, argv=<optimized out>) at MapMakerMainFunc.mm:29

2)
Breakpoint 4, objc_exception_throw (object=0x1715ac80) at /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/eh_personality.c:161
161 SEL rethrow_sel = sel_registerName("rethrow");
(gdb) bt
#0  objc_exception_throw (object=0x1715ac80) at /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/eh_personality.c:161
#1  0x00007ffff5cc5188 in -[NSException raise] (self=0x1715ac80, _cmd=<optimized out>) at NSException.m:1148
#2  0x00007ffff5cc4af0 in +[NSException raise:format:] (self=0x1715ac80, _cmd=<optimized out>, name=0x0, format=0x132295a0) at NSException.m:1016
#3  0x00000000004d2745 in parsePingTreeSymbol(NSString*, void*) (symbol=0xa478dd0, context=0x2e17390) at PingState.mm:83
...

3)
Breakpoint 1, -[NSException raise] (self=0x1715ac80, _cmd=0x7ffff74ed618 <.objc_selector_list+480>) at NSException.m:1137
1137  if (_reserved == 0)
(gdb) bt
#0  -[NSException raise] (self=0x1715ac80, _cmd=0x7ffff74ed618 <.objc_selector_list+480>) at NSException.m:1137
#1  0x00007ffff6f282ea in -[RadixTree initWithFileDescriptor:symbolParserFunction:context:] (self=0x104f940, _cmd=<optimized out>, fd=<optimized out>,
    parser=0x4d2410 <parsePingTreeSymbol(NSString*, void*)>, context=0x2e17390) at RadixTree.mm:847
#2  0x00000000004d23c7 in -[PingTreeParser pingTreeWithFile:] (self=0x0, _cmd=<optimized out>, path=0xa44fff8) at PingState.mm:305
...

4)
Breakpoint 4, objc_exception_throw (object=0x1715ac80) at /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/eh_personality.c:161
161 SEL rethrow_sel = sel_registerName("rethrow");
(gdb) bt
#0  objc_exception_throw (object=0x1715ac80) at /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/eh_personality.c:161
#1  0x00007ffff5cc5188 in -[NSException raise] (self=0x1715ac80, _cmd=<optimized out>) at NSException.m:1148
#2  0x00007ffff6f282ea in -[RadixTree initWithFileDescriptor:symbolParserFunction:context:] (self=0x104f940, _cmd=<optimized out>, fd=<optimized out>,
    parser=0x4d2410 <parsePingTreeSymbol(NSString*, void*)>, context=0x2e17390) at RadixTree.mm:847
#3  0x00000000004d23c7 in -[PingTreeParser pingTreeWithFile:] (self=0x0, _cmd=<optimized out>, path=0xa44fff8) at PingState.mm:305
...

5)
Breakpoint 1, -[NSException raise] (self=0x1715ac80, _cmd=0x7ffff74ed618 <.objc_selector_list+480>) at NSException.m:1137
1137  if (_reserved == 0)
(gdb) bt
#0  -[NSException raise] (self=0x1715ac80, _cmd=0x7ffff74ed618 <.objc_selector_list+480>) at NSException.m:1137
#1  0x00007ffff6f284d4 in -[RadixTree initWithFileDescriptor:symbolParserFunction:context:] (self=0xa4777a0, _cmd=<optimized out>, fd=<optimized out>,
    parser=0x4d2410 <parsePingTreeSymbol(NSString*, void*)>, context=0x2e17390) at RadixTree.mm:862
#2  0x00000000004d23c7 in -[PingTreeParser pingTreeWithFile:] (self=0x0, _cmd=<optimized out>, path=0xa44fff8) at PingState.mm:305
#3  0x00007ffff6ecc867 in -[FileAcceptor checkForObject] (self=0x2e16db0, _cmd=<optimized out>) at FileAcceptor.m:630
...

6)
Breakpoint 4, objc_exception_throw (object=0x1715ac80) at /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/eh_personality.c:161
161 SEL rethrow_sel = sel_registerName("rethrow");
(gdb) bt
#0  objc_exception_throw (object=0x1715ac80) at /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/eh_personality.c:161
#1  0x00007ffff5cc5188 in -[NSException raise] (self=0x1715ac80, _cmd=<optimized out>) at NSException.m:1148
#2  0x00007ffff6f284d4 in -[RadixTree initWithFileDescriptor:symbolParserFunction:context:] (self=0xa4777a0, _cmd=<optimized out>, fd=<optimized out>,
    parser=0x4d2410 <parsePingTreeSymbol(NSString*, void*)>, context=0x2e17390) at RadixTree.mm:862
#3  0x00000000004d23c7 in -[PingTreeParser pingTreeWithFile:] (self=0x0, _cmd=<optimized out>, path=0xa44fff8) at PingState.mm:305
#4  0x00007ffff6ecc867 in -[FileAcceptor checkForObject] (self=0x2e16db0, _cmd=<optimized out>) at FileAcceptor.m:630
...

7) First call to _Unwind_Resume, as we go one frame up the stack:

Breakpoint 2, _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:224
224 ../../../src/libgcc/unwind.inc: No such file or directory.
(gdb) bt
#0  _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:224
#1  0x00007ffff6f2850d in -[RadixTree initWithFileDescriptor:symbolParserFunction:context:] (self=<optimized out>, _cmd=<optimized out>, fd=<optimized out>,
    parser=0x4d2410 <parsePingTreeSymbol(NSString*, void*)>, context=0x2e17390) at RadixTree.mm:863
#2  0x00000000004d23c7 in -[PingTreeParser pingTreeWithFile:] (self=0x7fffffffb998, _cmd=<optimized out>, path=0x0) at PingState.mm:305
#3  0x00007ffff6ecc867 in -[FileAcceptor checkForObject] (self=0x2e16db0, _cmd=<optimized out>) at FileAcceptor.m:630
#4  0x00000000004ccd0c in PingScoresRebaser::rebase() (this=0x359e7f0) at PingScoresRebaser.mm:208
...

8)
Breakpoint 2, _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:224
224 in ../../../src/libgcc/unwind.inc
(gdb) bt
#0  _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:224
#1  0x00007ffff6ecc990 in -[FileAcceptor checkForObject] (self=0x2e16db0, _cmd=<optimized out>) at FileAcceptor.m:639
#2  0x00000000004ccd0c in PingScoresRebaser::rebase() (this=0x359e7f0) at PingScoresRebaser.mm:208
#3  0x00000000004c301b in RebaserProxy<PingScoresRebaser>::rebase() (this=<optimized out>) at ./RebaserProxy.h:202
#4  0x00000000004c1cb7 in RebaserProxy<PingScoresRebaser>::operator->() (this=0xf30f60) at ./RebaserProxy.h:262
#5  0x00000000004bda9a in -[MapMaker readNewInputs] (self=0xb645b0, _cmd=<optimized out>) at MapMaker.mm:1708
...

9)
Breakpoint 2, _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:224
224 in ../../../src/libgcc/unwind.inc
(gdb) bt
#0  _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:224
#1  0x00000000004bdb86 in -[MapMaker readNewInputs] (self=0xb645b0, _cmd=<optimized out>) at MapMaker.mm:1721
#2  0x00000000004bdbad in -[MapMaker readInputsAndMakeMap] (self=0xb645b0, _cmd=<optimized out>) at MapMaker.mm:1739
#3  0x00000000004bee19 in -[MapMaker doWork] (self=0xb645b0, _cmd=<optimized out>) at MapMaker.mm:2017
...

10)
Breakpoint 2, _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:224
224 in ../../../src/libgcc/unwind.inc
(gdb) bt
#0  _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:224
#1  0x00007ffff6e9bf10 in -[AkamaiDaemon(PrivateMethods) doOneCycle:] (self=0xb645b0, _cmd=<optimized out>, ignored=<optimized out>) at AkamaiDaemon.m:470
#2  0x00007ffff5d58897 in -[NSTimer fire] (self=0x1037e30, _cmd=<optimized out>) at NSTimer.m:283
#3  0x00007ffff5d33059 in -[NSRunLoop _limitDateForContext:] (self=0xe14760, _cmd=<optimized out>, context=0xe29850) at NSRunLoop.m:1011
...

11)
224 in ../../../src/libgcc/unwind.inc
(gdb) bt
#0  _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:224
#1  0x00007ffff5d58a0c in -[NSTimer fire] (self=0x1037e30, _cmd=<optimized out>) at NSTimer.m:287
#2  0x00007ffff5d33059 in -[NSRunLoop _limitDateForContext:] (self=0xe14760, _cmd=<optimized out>, context=0xe29850) at NSRunLoop.m:1011
#3  0x00007ffff5d33498 in -[NSRunLoop limitDateForMode:] (self=0xe14760, _cmd=0x7ffff61b2d58 <.objc_selector_list+160>, mode=0x7ffff61b1e38 <.objc_str>)
    at NSRunLoop.m:1111
...

12)
Breakpoint 2, _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:224
224 in ../../../src/libgcc/unwind.inc
(gdb) bt
#0  _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:224
#1  0x00007ffff5d33585 in -[NSRunLoop limitDateForMode:] (self=0xe14760, _cmd=0x7ffff61b2d58 <.objc_selector_list+160>, mode=0x7ffff61b1e38 <.objc_str>)
    at NSRunLoop.m:1119
#2  0x00007ffff5d33c56 in -[NSRunLoop runMode:beforeDate:] (self=0xe14760, _cmd=0x7ffff61b2df8 <.objc_selector_list+320>, mode=0x7ffff61b1e38 <.objc_str>,
    date=0xfcaff0) at NSRunLoop.m:1285
...

13)
Breakpoint 3, __GI_abort () at abort.c:51
51 abort.c: No such file or directory.
(gdb) bt
#0  __GI_abort () at abort.c:51
#1  0x00007ffff4f6f521 in _Unwind_Resume (exc=0xa6df4e0) at ../../../src/libgcc/unwind.inc:234
#2  0x00007ffff5d33585 in -[NSRunLoop limitDateForMode:] (self=0xe14760, _cmd=0x7ffff61b2d58 <.objc_selector_list+160>, mode=0x7ffff61b1e38 <.objc_str>)
    at NSRunLoop.m:1119
#3  0x00007ffff5d33c56 in -[NSRunLoop runMode:beforeDate:] (self=0xe14760, _cmd=0x7ffff61b2df8 <.objc_selector_list+320>, mode=0x7ffff61b1e38 <.objc_str>,
    date=0xfcaff0) at NSRunLoop.m:1285

> There are also a bunch of debug logs that are compiled out in the EH code - if you #define DEBUG_LOG at the top of eh_personality.c then you should see them and get a better idea of what’s going on.

I did this, and I've pasted the output below.  At first glance, the exceptions all seem to be the same (or at least, all have the same address of 0x17158c80).  I'm not entirely sure how to interpret the rest of the action there.  One thing that leaps out is that there seem to be personalities involved: GNUCOBJC and GNUCC++.  The code that throws this exception is a .mm file, i.e., Objective-C++, with a mix of ObjC and C++.  

Any help is appreciated!

Thanks,

David

Log contents with debugging enabled:

Log at first breakpoint:
Throwing 0x17158c80

At second:
Throwing 0x17158c80
Throwing 0x17158c80

At third:
Throwing 0x17158c80
Throwing 0x17158c80
Throwing 0x17158c80
New personality function called 0xa477680
Class: GNUCOBJC
LSDA: 0x7ffff721df44
Search phase...
Filter: 1
Class name: NSException
0x7ffff6177de8 type: 1
found handler for NSException
handler: 4
Found handler! 4
New personality function called 0xa6dddf0
Class: GNUCC++
Foreign class: 0x7ffff6136a28
LSDA: 0x7ffff721df44
Phase 2: Fight!
Not the handler frame, looking up the cleanup again
Filter: 1
Class name: NSException
0x7ffff6177de8 type: 0
Filter: 0
0 filter
handler! 1 0
Installing cleanup...
Installing context, selector 0
New personality function called 0xa6dddf0
Class: GNUCC++
Foreign class: 0x7ffff6136a28
LSDA: 0x7ffff721df44
Phase 2: Fight!
Not the handler frame, looking up the cleanup again
New personality function called 0xa6dddf0
Class: GNUCC++
Foreign class: 0x7ffff6136a28
LSDA: 0x7ffff721ba0c
Phase 2: Fight!
Not the handler frame, looking up the cleanup again
Filter: 1
Class name: NSException
0x7ffff6177de8 type: 0
Filter: 0
0 filter
handler! 1 0
Installing cleanup...
Installing context, selector 0
New personality function called 0xa6dddf0
Class: GNUCC++
Foreign class: 0x7ffff6136a28
LSDA: 0x7ffff721ba0c
Phase 2: Fight!
Not the handler frame, looking up the cleanup again
New personality function called 0xa6dddf0
Class: GNUCC++
Foreign class: 0x7ffff6136a28
LSDA: 0x7ffff5f28ca4
Phase 2: Fight!
Not the handler frame, looking up the cleanup again
Filter: 1
Class name: NSException
0x7ffff6177de8 type: 0
Filter: 0
0 filter
handler! 1 0
Installing cleanup...
Installing context, selector 0
New personality function called 0xa6dddf0
Class: GNUCC++
Foreign class: 0x7ffff6136a28
LSDA: 0x7ffff5f28ca4
Phase 2: Fight!
Not the handler frame, looking up the cleanup again
New personality function called 0xa6dddf0
Class: GNUCC++
Foreign class: 0x7ffff6136a28
LSDA: 0x7ffff5f28300
Phase 2: Fight!
Not the handler frame, looking up the cleanup again
Filter: 1
Class name: NSException
0x7ffff6177de8 type: 0
Filter: 0
0 filter
handler! 1 0
Installing cleanup...
Installing context, selector 0
New personality function called 0xa6dddf0
Class: GNUCC++
Foreign class: 0x7ffff6136a28
LSDA: 0x7ffff5f28300
Phase 2: Fight!
Not the handler frame, looking up the cleanup again

At this point, abort is called.






_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

David Chisnall
On 7 Dec 2017, at 19:54, Lobron, David <[hidden email]> wrote:
>
> I did this, and I've pasted the output below.  At first glance, the exceptions all seem to be the same (or at least, all have the same address of 0x17158c80).  I'm not entirely sure how to interpret the rest of the action there.  One thing that leaps out is that there seem to be personalities involved: GNUCOBJC and GNUCC++.  The code that throws this exception is a .mm file, i.e., Objective-C++, with a mix of ObjC and C++.  

Aha!  That might be it, and might be enough to come up with a reduced test case.  When you throw the exception, it’s an ObjC exception.  When you throw it through an ObjC++ stack frame, it’s transformed into a C++ exception and passed to the C++ personality function, but only when the cleanup is run for that frame.  I suspect that the ObjC personality function is not correctly handling the transformation back to an ObjC exception for the catch.

David


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

Lobron, David

>> I did this, and I've pasted the output below.  At first glance, the exceptions all seem to be the same (or at least, all have the same address of 0x17158c80).  I'm not entirely sure how to interpret the rest of the action there.  One thing that leaps out is that there seem to be personalities involved: GNUCOBJC and GNUCC++.  The code that throws this exception is a .mm file, i.e., Objective-C++, with a mix of ObjC and C++.  
>
> Aha!  That might be it, and might be enough to come up with a reduced test case.  When you throw the exception, it’s an ObjC exception.  When you throw it through an ObjC++ stack frame, it’s transformed into a C++ exception and passed to the C++ personality function, but only when the cleanup is run for that frame.  I suspect that the ObjC personality function is not correctly handling the transformation back to an ObjC exception for the catch.

Thanks, David!  I will try to make a reduced test case, based on this.  I'll start with a simple program that throws an ObjC exception in an NS_HANDLER, where the whole thing resides in a .mm file.  BTW, I've enabled native exceptions here.  I will let you guys know when I've got something.

Thank you again for your help,

David
_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

David Chisnall
On 7 Dec 2017, at 20:38, Lobron, David <[hidden email]> wrote:
>
> Thanks, David!  I will try to make a reduced test case, based on this.  I'll start with a simple program that throws an ObjC exception in an NS_HANDLER, where the whole thing resides in a .mm file.  BTW, I've enabled native exceptions here.  I will let you guys know when I've got something.

Just use @try and @catch.  If you can find a smallish test case, please file a bug on GitHub and I’ll take a look.  Ideally, a test case shouldn’t depend on anything other than libobjc2 - take a look at the existing EH tests and see if you can make a similar one that shows the issue.

David


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

Lobron, David
Hi David-

>> Thanks, David!  I will try to make a reduced test case, based on this.  I'll start with a simple program that throws an ObjC exception in an NS_HANDLER, where the whole thing resides in a .mm file.  BTW, I've enabled native exceptions here.  I will let you guys know when I've got something.
>
> Just use @try and @catch.  If you can find a smallish test case, please file a bug on GitHub and I’ll take a look.  Ideally, a test case shouldn’t depend on anything other than libobjc2 - take a look at the existing EH tests and see if you can make a similar one that shows the issue.

I've been trying combinations of C++ and ObjC exceptions in a .mm file, and so far I haven't been able to reproduce the abort behavior.  I've attached my latest code, which throws ObjC and C++ exceptions from C++ and ObjC classes, and catches them.  Please let me know if you can think of any variations I haven't tried here.  

I tried setting breakpoints on -[NSException raise] and objc_exception_throw/objc_exception_rethrow: the stack traces looked similar to the ones from the program that was crashing (copied below).  At the least, I don't see anything obvious in those stack traces to suggest where the bug might lie.

--David

Breakpoint 1, -[NSException raise] (self=0x747070, _cmd=0x7ffff7d29130 <.objc_selector_list+256>) at NSException.m:1137
1137  if (_reserved == 0)
(gdb) bt
#0  -[NSException raise] (self=0x747070, _cmd=0x7ffff7d29130 <.objc_selector_list+256>) at NSException.m:1137
#1  0x00007ffff7874ac0 in +[NSException raise:format:] (self=0x747070, _cmd=<optimized out>, name=0x73f148, format=0x3) at NSException.m:1016
#2  0x0000000000401325 in -[ObjcClass poke] ()
#3  0x0000000000401507 in main ()

Breakpoint 4, objc_exception_throw (object=0x747070) at /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/eh_personality.c:163
163 SEL rethrow_sel = sel_registerName("rethrow");
(gdb) bt
#0  objc_exception_throw (object=0x747070) at /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/eh_personality.c:163
#1  0x00007ffff7875158 in -[NSException raise] (self=0x747070, _cmd=<optimized out>) at NSException.m:1148
#2  0x00007ffff7874ac0 in +[NSException raise:format:] (self=0x747070, _cmd=<optimized out>, name=0x7ffff66e6778 <main_arena+24>, format=0x7470a0) at NSException.m:1016
#3  0x0000000000401325 in -[ObjcClass poke] ()
#4  0x0000000000401507 in main ()

Breakpoint 4, objc_exception_throw (object=0x8f4630) at /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/eh_personality.c:163
163 SEL rethrow_sel = sel_registerName("rethrow");
(gdb) bt
#0  objc_exception_throw (object=0x8f4630) at /home/dlobron/build/clangport/akamai/libobjc2/libobjc2-1.8.1/eh_personality.c:163
#1  0x00007ffff7875158 in -[NSException raise] (self=0x8f4630, _cmd=<optimized out>) at NSException.m:1148
#2  0x00007ffff7874ac0 in +[NSException raise:format:] (self=0x8f4630, _cmd=<optimized out>, name=0x7ffff66e6778 <main_arena+24>, format=0x82f520) at NSException.m:1016
#3  0x0000000000401900 in CppClass::poke() const ()
#4  0x000000000040156f in main ()



_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep

ExceptionPersonality.mm (2K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

David Chisnall
On 11 Dec 2017, at 16:57, Lobron, David <[hidden email]> wrote:
>
>>> Thanks, David!  I will try to make a reduced test case, based on this.  I'll start with a simple program that throws an ObjC exception in an NS_HANDLER, where the whole thing resides in a .mm file.  BTW, I've enabled native exceptions here.  I will let you guys know when I've got something.
>>
>> Just use @try and @catch.  If you can find a smallish test case, please file a bug on GitHub and I’ll take a look.  Ideally, a test case shouldn’t depend on anything other than libobjc2 - take a look at the existing EH tests and see if you can make a similar one that shows the issue.
>
> I've been trying combinations of C++ and ObjC exceptions in a .mm file, and so far I haven't been able to reproduce the abort behavior.  I've attached my latest code, which throws ObjC and C++ exceptions from C++ and ObjC classes, and catches them.  Please let me know if you can think of any variations I haven't tried here.  
>

If the failure is what I suspect that it is, then it’s a problem caused by mixing C++ and Objective-C exception unwinding, so it won’t be possible to reproduce in a single file.  You will need to throw an exception from Objective-C++ and have it pass through stack frames from an Objective-C compilation unit that includes an @finally block that runs some code, and then be caught in an Objective-C++ compilation unit.  

David


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

Lobron, David

>>> Just use @try and @catch.  If you can find a smallish test case, please file a bug on GitHub and I’ll take a look.  Ideally, a test case shouldn’t depend on anything other than libobjc2 - take a look at the existing EH tests and see if you can make a similar one that shows the issue.
>>
>> I've been trying combinations of C++ and ObjC exceptions in a .mm file, and so far I haven't been able to reproduce the abort behavior.  I've attached my latest code, which throws ObjC and C++ exceptions from C++ and ObjC classes, and catches them.  Please let me know if you can think of any variations I haven't tried here.  
>>
>
> If the failure is what I suspect that it is, then it’s a problem caused by mixing C++ and Objective-C exception unwinding, so it won’t be possible to reproduce in a single file.  You will need to throw an exception from Objective-C++ and have it pass through stack frames from an Objective-C compilation unit that includes an @finally block that runs some code, and then be caught in an Objective-C++ compilation unit.  

Ah, got it.  I will try this.  

Would separate .o files linked into a single binary be sufficient for counting as separate compilation units?  Or would it be better to compile and link a shared library, and pick it up at runtime?

--David
_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

David Chisnall

> On 11 Dec 2017, at 17:09, Lobron, David <[hidden email]> wrote:
>
>
>>>> Just use @try and @catch.  If you can find a smallish test case, please file a bug on GitHub and I’ll take a look.  Ideally, a test case shouldn’t depend on anything other than libobjc2 - take a look at the existing EH tests and see if you can make a similar one that shows the issue.
>>>
>>> I've been trying combinations of C++ and ObjC exceptions in a .mm file, and so far I haven't been able to reproduce the abort behavior.  I've attached my latest code, which throws ObjC and C++ exceptions from C++ and ObjC classes, and catches them.  Please let me know if you can think of any variations I haven't tried here.  
>>>
>>
>> If the failure is what I suspect that it is, then it’s a problem caused by mixing C++ and Objective-C exception unwinding, so it won’t be possible to reproduce in a single file.  You will need to throw an exception from Objective-C++ and have it pass through stack frames from an Objective-C compilation unit that includes an @finally block that runs some code, and then be caught in an Objective-C++ compilation unit.  
>
> Ah, got it.  I will try this.  
>
> Would separate .o files linked into a single binary be sufficient for counting as separate compilation units?

Yup, that’s fine.

David



_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

Lobron, David
Hey David-

>>> If the failure is what I suspect that it is, then it’s a problem caused by mixing C++ and Objective-C exception unwinding, so it won’t be possible to reproduce in a single file.  You will need to throw an exception from Objective-C++ and have it pass through stack frames from an Objective-C compilation unit that includes an @finally block that runs some code, and then be caught in an Objective-C++ compilation unit.  

I created three files: a .mm file that contains main(), a .mm that throws an ObjC exception, and a .m that passes the ObjC exception through from a @finally block back to the .mm file that contains main.  I think I'm getting closer, because my debug output now has several "Fight!" clauses (see below) but all the personality classes are of type GNUCOBJC.  I haven't been able to coax it to emit GNUCC++.

I've attached the three files, which I compile into separate .o files and then link (compile/link command is also attached).

Do you have any other ideas for how I can get an exception of "Class: GNUCC++" to appear, instead of just GNUCOBJC?

Thank you,

David


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep

compile.sh (3K) Download Attachment
ExceptionPersonality.mm (1K) Download Attachment
RunTest.mm (1K) Download Attachment
TestExceptionPersonality.m (818 bytes) Download Attachment
ATT00001.txt (3K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

David Chisnall
On 11 Dec 2017, at 20:10, Lobron, David <[hidden email]> wrote:
>
>>>> If the failure is what I suspect that it is, then it’s a problem caused by mixing C++ and Objective-C exception unwinding, so it won’t be possible to reproduce in a single file.  You will need to throw an exception from Objective-C++ and have it pass through stack frames from an Objective-C compilation unit that includes an @finally block that runs some code, and then be caught in an Objective-C++ compilation unit.  
>
> I created three files: a .mm file that contains main(), a .mm that throws an ObjC exception, and a .m that passes the ObjC exception through from a @finally block back to the .mm file that contains main.  I think I'm getting closer, because my debug output now has several "Fight!" clauses (see below) but all the personality classes are of type GNUCOBJC.  I haven't been able to coax it to emit GNUCC++.
>
> I've attached the three files, which I compile into separate .o files and then link (compile/link command is also attached).
>
> Do you have any other ideas for how I can get an exception of "Class: GNUCC++" to appear, instead of just GNUCOBJC?

Throwing with `throw` instead of `@throw` should do that: the exception will be thrown by the C++ runtime, not the Objective-C one.  I thought your problem was the other way around though, so it might be that you need a c++ destructor to force the ObjC runtime to turn it into a C++ exception for delivery to C++.

David


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

Lobron, David

> Throwing with `throw` instead of `@throw` should do that: the exception will be thrown by the C++ runtime, not the Objective-C one.  I thought your problem was the other way around though, so it might be that you need a c++ destructor to force the ObjC runtime to turn it into a C++ exception for delivery to C++.

I tweaked my code a bit.  I now have an ObjC class in a .m file that throws an exception like this:

- (void)poke
{
  [NSException raise:@"Exception" format:@"Exception"];
}

I call this from a .m file, inside an NS_HANDLER, and then re-raise the exception.  The goal here is to pass the NSException through a plain ObjC frame:

- (void)poke
{
    NS_DURING
      [_oc poke];
    NS_HANDLER
      NSLog(@"Caught second exception");
      [localException raise];
    NS_ENDHANDLER
}

Finally, I call this from a .mm file, inside a C++ class method:

void tryCpp() {
    Tester *t = [[Tester new] autorelease];
    try {
      try {
        NSLog(@"tryCpp starting");
        [t poke];
      } catch(NSException *e) {
        NSLog(@"tryCpp aught ObjC exception");
        throw e;
      }
    } catch(...) {
      NSLog(@"tryCpp caught outer exception");
    }
  }

The goal here is to do what you suggested above, namely, force the ObjC runtime to turn the NSException into a C++ exception for delivery to the tryCpp method, which is a plain C++ instance method.  But the debug output (copied below) suggests that is not happening: all of the exception personalities are of class GNUCOBJC.  Am I missing a step here, or not understanding the way exception personalities are passed through frames?  I'm still learning this, so maybe I'm missing something obvious.

Thanks,

--David

Debug output:

2017-12-12 15:28:01.629 RunTest[19196:19196] tryCpp starting
Throwing 0x17cf610
New personality function called 0x17db3a0
Class: GNUCOBJC
LSDA: 0x40405c
Search phase...
Filter: 1
Class name: NSException
0x7f3316d04de8 type: 1
found handler for NSException
handler: 4
Found handler! 4
New personality function called 0x17db3a0
Class: GNUCOBJC
LSDA: 0x40405c
Phase 2: Fight!
Installing context, selector 1
Beginning catch 0x17db3a0
objc catch
2017-12-12 15:28:01.629 RunTest[19196:19196] Caught second exception
Throwing 0x17cf610
New personality function called 0x17db440
Class: GNUCOBJC
LSDA: 0x40405c
Search phase...
handler: 1
New personality function called 0x17db440
Class: GNUCOBJC
LSDA: 0x40405c
Phase 2: Fight!
Not the handler frame, looking up the cleanup again
handler! 1 0
Installing cleanup...
Installing context, selector 0
Ending catch
New personality function called 0x17db440
Class: GNUCOBJC
LSDA: 0x40405c
Phase 2: Fight!
Not the handler frame, looking up the cleanup again
2017-12-12 15:28:01.629 RunTest[19196:19196] tryCpp aught ObjC exception
Throwing 0x17cf610
2017-12-12 15:28:01.629 RunTest[19196:19196] tryCpp caught outer exception



_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

David Chisnall
On 12 Dec 2017, at 15:30, Lobron, David <[hidden email]> wrote:
>
> I tweaked my code a bit.  I now have an ObjC class in a .m file that throws an exception like this:
>
> - (void)poke
> {
>  [NSException raise:@"Exception" format:@"Exception"];
> }

This doesn’t add anything, because the exception is thrown from the NSException -raise method, which is ObjC and will do a @throw from an ObjC compilation unit inside -base.  For a libobjc2 test, I’ll need it to not depend on -base at all when I add it to the test suite.

> I call this from a .m file, inside an NS_HANDLER, and then re-raise the exception.  The goal here is to pass the NSException through a plain ObjC frame:
>
> - (void)poke
> {
>    NS_DURING
>      [_oc poke];
>    NS_HANDLER
>      NSLog(@"Caught second exception");
>      [localException raise];
>    NS_ENDHANDLER
> }


This is easier to read if you don’t use the macros.  Now you’re doing @catch(id), which is catching the exception.  You’re then throwing it again (@throw inside the -raise method), rather that rethrowing it (@throw with no argument).  This wants to be an @finally though, because we’re looking for a case where the search phase and the unwinding phase do different things.

>
> Finally, I call this from a .mm file, inside a C++ class method:
>
> void tryCpp() {
>    Tester *t = [[Tester new] autorelease];
>    try {
>      try {
>        NSLog(@"tryCpp starting");
>        [t poke];
>      } catch(NSException *e) {
>        NSLog(@"tryCpp aught ObjC exception");
>        throw e;
>      }
>    } catch(...) {
>      NSLog(@"tryCpp caught outer exception");
>    }
>  }
>
> The goal here is to do what you suggested above, namely, force the ObjC runtime to turn the NSException into a C++ exception for delivery to the tryCpp method, which is a plain C++ instance method.  But the debug output (copied below) suggests that is not happening: all of the exception personalities are of class GNUCOBJC.  Am I missing a step here, or not understanding the way exception personalities are passed through frames?  I'm still learning this, so maybe I'm missing something obvious.

Try to recreate the structure of your original failure with the same mix of ObjC/C++/ObjC++ stack frames and the same cleanups / catches.

David


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

Lobron, David
Hi David-

>> The goal here is to do what you suggested above, namely, force the ObjC runtime to turn the NSException into a C++ exception for delivery to the tryCpp method, which is a plain C++ instance method.  But the debug output (copied below) suggests that is not happening: all of the exception personalities are of class GNUCOBJC.  Am I missing a step here, or not understanding the way exception personalities are passed through frames?  I'm still learning this, so maybe I'm missing something obvious.
>
> Try to recreate the structure of your original failure with the same mix of ObjC/C++/ObjC++ stack frames and the same cleanups / catches.

I've got a working unit test in the libobjc2 Test directory, which depends only on libobjc2.  I'm trying to reproduce my original failure by doing the following:

1. Throw an exception from a .mm file.

2. Pass it through a .m file.

3. Catch the exception in a C++ class in another .mm file.

I tried a few versions of this, including catching the exception inside a C++ class, but so far I have not been able to induce the runtime to coerce a GNUCOBJC exception into a GNUCC++ one.  The debug logs report:

New personality function called 0xd28300
Class: GNUCOBJC
LSDA: 0x4024ec
Search phase...
handler: 1
New personality function called 0xd28300
Class: GNUCOBJC
LSDA: 0x4024ec
Phase 2: Fight!
Not the handler frame, looking up the cleanup again
Caught exception in C++

Are there certain code blocks that might make it more likely for the runtime to turn GNUCOBJC into GNUC++?  I've tried throwing the ObjC exception from inside a C++ try/catch block, but there's no GNUC++ exception in sight.  I've attached the files for this test, and the modified version of the CMakeLists.txt needed to build it (there's also a modified version of Test.h that separates class implementation from interface, since I'm using the class in multiple files for this test).

--David


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep

Chowda1.h (178 bytes) Download Attachment
Chowda1.mm (292 bytes) Download Attachment
Chowda2.h (132 bytes) Download Attachment
Chowda2.m (424 bytes) Download Attachment
Chowda3.mm (942 bytes) Download Attachment
Clam.h (166 bytes) Download Attachment
Clam.mm (162 bytes) Download Attachment
CMakeLists.txt (2K) Download Attachment
ModTest.h (658 bytes) Download Attachment
ModTest.m (892 bytes) Download Attachment
ATT00001.txt (18 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Mysterious crash in NSRunLoop, using libobjc2 on Linux

David Chisnall

> On 18 Dec 2017, at 20:04, Lobron, David <[hidden email]> wrote:
>
> Hi David-
>
>>> The goal here is to do what you suggested above, namely, force the ObjC runtime to turn the NSException into a C++ exception for delivery to the tryCpp method, which is a plain C++ instance method.  But the debug output (copied below) suggests that is not happening: all of the exception personalities are of class GNUCOBJC.  Am I missing a step here, or not understanding the way exception personalities are passed through frames?  I'm still learning this, so maybe I'm missing something obvious.
>>
>> Try to recreate the structure of your original failure with the same mix of ObjC/C++/ObjC++ stack frames and the same cleanups / catches.
>
> I've got a working unit test in the libobjc2 Test directory, which depends only on libobjc2.

That’s great.  When you have it in a failing form, please can you send me a pull request containing the test?

>  I'm trying to reproduce my original failure by doing the following:
>
> 1. Throw an exception from a .mm file.
>
> 2. Pass it through a .m file.
>
> 3. Catch the exception in a C++ class in another .mm file.
>
> I tried a few versions of this, including catching the exception inside a C++ class, but so far I have not been able to induce the runtime to coerce a GNUCOBJC exception into a GNUCC++ one.  The debug logs report:
>
> New personality function called 0xd28300
> Class: GNUCOBJC
> LSDA: 0x4024ec
> Search phase...
> handler: 1
> New personality function called 0xd28300
> Class: GNUCOBJC
> LSDA: 0x4024ec
> Phase 2: Fight!
> Not the handler frame, looking up the cleanup again
> Caught exception in C++

These might be slightly misleading, because you only get these debug messages from ObjC stack frames, not from C++ or ObjC++.  It might help you to add some DEBUG_LOG statements to __gnustep_objcxx_personality_v0.

Looking at the code, it appears that the exception is coerced to a C++ exception if it is thrown from an ObjC stack frame.  Everything else looks as if it ought to be able to handle either kind of exception though, so I’m still somewhat confused as to the cause of this one.

David


_______________________________________________
Discuss-gnustep mailing list
[hidden email]
https://lists.gnu.org/mailman/listinfo/discuss-gnustep
12