java-native-access / jna

Java Native Access
Other
8.53k stars 1.68k forks source link

JNA should include std::exception::what() in thrown `java.lang.Error` #1192

Open Groostav opened 4 years ago

Groostav commented 4 years ago

As a caller of a cpp hunk of math I want std::exceptions to be nicely translated by JNA across the bindings so that I can more easily debug runtime problems.

I have some cpp code that I've updated to throw std::exception's that contain valuable data in their what() clause. What I was hoping JNA would do is this:

//mylib.dll
#include <exception>

extern "C" __declspec(dllexport) 
const void MYCOMPANY_compute_eigen_values_or_something()
{
  throw std::exception("message from native code: kablamo!")
}
interface MyLib: Library {
  public void MYCOMPANY_compute_eigen_values_or_something()
}
var lib = Native.loadLibrary("mylib", MyLib.class, options)
try { lib.MYCOMPANY_compute_eigen_values_or_something() }
catch(Throwable ex){
  System.out.println(ex)
}

expected: java.lang.Error: In native code: message from native code: kablamo! actual: java.lang.Error: Invalid memory access

... on: Windows 10 1903, JNA 4.2.2


My work around: Wrap my code in a try { } catch { }, save it to a global variable, and expose a function GetLastError() that I then call from the handler. If you combine this with StackWalker you can even get a java--ish native code stack trace in your preconditions/postconditions. From the java side, I add my own InvocationMapper to catch JNA thrown java.lang.Errors, call GetLastError() then throw a new exception with the results.

Groostav commented 4 years ago

I'm sorry I talked to my native-code friend and of course the problem is C. C does not support exceptions! On Windows (and in C) Structured Exception Handling will let JNA capture CPP style exceptions, but isn't going to let you into a cpp v-table to call exception::what without serious hacks.

But I suspect that error message "Invalid Memory access" comes from the fact that SEH-handler code is hitting the same JNA code path as s SIG_SEGV. This would seem to be an error. If JNA catches a SIG_SEGV, it should report "Invalid Memory access", but if it encounters an SEH exception, could it report "user thrown exception" or something like that?

Groostav commented 4 years ago

ahh, but with SEH the std::exception instance does appear recoverable in (_EXCEPTION_POINTERS *ep)->ExceptionRecord->ExceptionInformation[1]:

#include <chrono>
#include <stdio.h>
#include <windows.h> 
#include <excpt.h>

// this was done at gtests own suggestion. 
#define _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING

namespace EMPOWER_LGO
{
    std::exception exception_reserve;

    int filter(struct _EXCEPTION_POINTERS *ep)
    {
        //I dont know what kind of memory we're in.
        // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf
        // But you cant access GetExceptionInformation from the __except block, only from the filter, so we'll have ot move it ourselves.
        exception_reserve = *(std::exception*)(ep->ExceptionRecord->ExceptionInformation[1]);
        return EXCEPTION_EXECUTE_HANDLER;
    }

    void trapException()
    {
        __try
        {
            throw std::exception("kablamo!");
        }
        __except (filter(GetExceptionInformation()))
        {
            // i cant make a local var nor can I do anything with ASSERT_STREQ here
            // because of C2712:
            // https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2712?view=vs-2019

            //anyways, under the debugger the values do appear correct. 
            auto y = 4;
        }
    }

    TEST(SEHFixture, WhenCallingSehThingsShouldThings)
    {
        //act
        trapException();

        //assert
        ASSERT_STREQ(exception_reserve.what(), "kablamo!");
    }
}
matthiasblaesing commented 4 years ago

Please keep in mind, that JNA is platform independend and SEH is windows only (to my knowledge). So any solution should keep that in mind. It would also be helpful to get this more structured, this is not QnA or discussion forum, but the issue tracker.

For discussions please come to the mailinglist. As a general rule: feature requests that are not take care by their authors have the tendency to not be worked on, so you should be prepared to get your hands dirty :-)

Groostav commented 4 years ago

Please keep in mind, that JNA is platform independend and SEH is windows only (to my knowledge). So any solution should keep that in mind. It would also be helpful to get this more structured, this is not QnA or discussion forum, but the issue tracker.

You are of course correct; SEH is a (not particularly good) exception-handling implementation used by both msvc and msvc++ compilers. It also exposes macros for C callers. As such I would of course have to wrap it in the appropriate IFDEF WIN32.

I'll create a topic on the mailing list.

Groostav commented 4 years ago

created a google groups discussion here: https://groups.google.com/forum/#!topic/jna-users/moTydETIf-A