Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to compile binary on linux with Julia 1.5.3 #2

Closed
subes opened this issue Dec 24, 2021 · 28 comments
Closed

Unable to compile binary on linux with Julia 1.5.3 #2

subes opened this issue Dec 24, 2021 · 28 comments

Comments

@subes
Copy link

subes commented Dec 24, 2021

See screenshot:
grafik

I would like to add a native julia integration to: https://github.com/invesdwin/invesdwin-context-julia/

@subes subes changed the title Unable to compile binary on linux with Julia 1.5.x Unable to compile binary on linux with Julia 1.5.3 Dec 24, 2021
@subes
Copy link
Author

subes commented Dec 24, 2021

I guess it will also fail with julia 1.6 and 1.7, though I did not try.

@rssdev10
Copy link
Owner

Thanks for the notice. The mentioned issue is easy to fix. See the branch https://github.com/rssdev10/julia4j/tree/julia1.7_java17

But there are some other issues with Java 17, Julia 1.7. Long time I didn't do update. Looks like julia.h is not more C compatible. I removed jl_generic_function_def and jl_method_def. Good that it is not something often used.

For now the native library might be assembled, but unfortunately I cannot check it on home Linux due to pretty old environment. There are issues with visibility of libraries and proper versions.

Other issues I see, my main OS is MacOS. Since last time there were changes in java 17 installation path, Julia 1.7 registered paths, and OS Big Sur is with security restrictions and changes in dynamic libraries loading. So, I can build the jar, but not able to configure the environment to provide visibility of dynamic libraries yet.

Please check from your side if you can.

@subes
Copy link
Author

subes commented Dec 24, 2021

Hi, thanks a lot for the modifications. I can now compile it. Though it seems the linker uses wrong paths:

/usr/java/packages/lib:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib:/usr/lib/jvm/java-16-openjdk-amd64/lib
ERROR: could not load library "/lib/x86_64-linux-gnu/../bin/../lib/x86_64-linux-gnu/julia/sys.so"
/lib/x86_64-linux-gnu/../bin/../lib/x86_64-linux-gnu/julia/sys.so: Kann die Shared-Object-Datei nicht öffnen: Datei oder Verzeichnis nicht gefunden

Why is the "../bin/.." part in the path? Maybe manually creating this folder makes things work, but this will not be portable. I guess static linking is also not a solution because this will embed the julia installation? Though maybe we could still try this as long as the .so does not become too large because of this?

@subes
Copy link
Author

subes commented Dec 24, 2021

Nope, creating the folder then makes another error pop up about a different path which can not be workarounded:

/usr/java/packages/lib:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib:/usr/lib/jvm/java-16-openjdk-amd64/lib
ERROR: could not load library "/usr/lib/bin/../lib/x86_64-linux-gnu/julia/sys.so"
/usr/lib/bin/../lib/x86_64-linux-gnu/julia/sys.so: Kann die Shared-Object-Datei nicht öffnen: Datei oder Verzeichnis nicht gefunden

@subes
Copy link
Author

subes commented Dec 24, 2021

Adding the jvm parameter " -Djava.library.path=/usr/lib/x86_64-linux-gnu/julia" also does not help:

/usr/lib/x86_64-linux-gnu/julia:/usr/lib/jvm/java-16-openjdk-amd64/lib
ERROR: could not load library "/lib/x86_64-linux-gnu/../bin/../lib/x86_64-linux-gnu/julia/sys.so"
/lib/x86_64-linux-gnu/../bin/../lib/x86_64-linux-gnu/julia/sys.so: Kann die Shared-Object-Datei nicht öffnen: Datei oder Verzeichnis nicht gefunden

So the wrong path seems to be hardcoded into the libjulia4j.so as a wrong path.

@subes
Copy link
Author

subes commented Dec 24, 2021

Here the testcase I am using: https://github.com/invesdwin/invesdwin-context-julia/blob/main/invesdwin-context-julia-parent/invesdwin-context-julia-runtime-julia4j/src/test/java/de/invesdwin/context/julia/runtime/juliacaller/Julia4JJNITest.java

If you want to test yourself, installing Ubuntu in VirtualBox could make it testable on linux for you. Otherwise I will try again if you have something testable.

@rssdev10
Copy link
Owner

We need to check https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling

I want to update my local OpenSUSE which I previously used. Now I'm also not able to update cmake due to breaking dependencies and not able to check the build process properly. But the OS update will take some time. VM is good when there is enough space on an internal laptop's drive...

@subes
Copy link
Author

subes commented Dec 25, 2021

I looked at the CMake scripts a bit more, I guess the JULIA_HOME part might be detected wrong:

subes@subes-lap julia4j/swig (julia1.7_java17 *%) » ./build.sh                                                                                                                                                                                                                                              1:10
/usr/lib/jvm/default-java/
-- Found Julia executable: /usr/bin/julia
-- Julia_VERSION_STRING: 1.5.3
-- Julia_INCLUDE_DIRS:   /usr/include/julia
-- Julia_LIBRARY_DIR:    /usr/lib/x86_64-linux-gnu
-- Julia_LIBRARY:        /usr/lib/x86_64-linux-gnu/libjulia.so.1
-- JULIA_HOME:           /usr/bin
-- Julia_LLVM_VERSION:   v9.0.1
-- Julia_WORD_SIZE:      64
...
-- Configuring done
WARNING: Target "julia4j" requests linking to directory "/usr/lib/x86_64-linux-gnu".  Targets may link only to libraries.  CMake is dropping the item.
-- Generating done
-- Build files have been written to: /home/subes/Dokumente/Entwicklung/invesdwin/julia4j/swig
...
Install the project...
-- Install configuration: ""
-- Installing: /home/subes/Dokumente/Entwicklung/invesdwin/julia4j/swig/../src/main/resources/native/64/linux/libjulia4j.so
-- Set runtime path of "/home/subes/Dokumente/Entwicklung/invesdwin/julia4j/swig/../src/main/resources/native/64/linux/libjulia4j.so" to ""

The cmake script thinks this is a julia installation which was installed from the website, though this installation comes from the apt/deb package manager and follows normal linux directory layouts where everything lies in a different place.
Though I cannot connect where JULIA_HOME is used to define paths for the linker. So maybe my guess is wrong.

I also tried using: set(CMAKE_INSTALL_RPATH "@loader_path")
which did not help in any way.
I also tried giving LD_LIBRARY_PATH and/or RUNPATH as env vars to the testcase but this also did not help.

@subes
Copy link
Author

subes commented Dec 25, 2021

Ok, it seems not to be a problem of cmake, instead Julia itself does not handle embedding well when running from debian packaged installations: JuliaInterop/JuliaCall#99
Though when applying the workaround:
Julia4J.jl_init_with_image("/usr/bin", null);

I get the following errors:

/usr/java/packages/lib:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib:/usr/lib/jvm/java-16-openjdk-amd64/lib
fatal: error thrown and no exception handler available.
ErrorException("could not load symbol "jl_n_threads":
/usr/lib/jvm/java-1.16.0-openjdk-amd64/bin/java: undefined symbol: jl_n_threads")
jl_errorf at /lib/x86_64-linux-gnu/libjulia.so.1 (unknown line)
jl_dlsym at /lib/x86_64-linux-gnu/libjulia.so.1 (unknown line)
jl_load_and_lookup at /lib/x86_64-linux-gnu/libjulia.so.1 (unknown line)
nthreads at ./threadingconstructs.jl:19 [inlined]
__preinit_threads__ at ./task.jl:516
jfptr___preinit_threads___33846.clone_1 at /usr/lib/x86_64-linux-gnu/julia/sys.so (unknown line)
unknown function (ip: 0x7f4bf2b3afbb)
jl_init_with_image__threading at /lib/x86_64-linux-gnu/libjulia.so.1 (unknown line)
Java_org_julia_jni_swig_Julia4JJNI_jl_1init_1with_1image at /tmp/nativeutils12953487237596/libjulia4j.so (unknown line)
unknown function (ip: 0x7f4c384acc7a)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c3849fcc8)
unknown function (ip: 0x7f4c56dd505e)
unknown function (ip: 0x7f4c573b6ce9)
JVM_InvokeMethod at /usr/lib/jvm/java-16-openjdk-amd64/lib/server/libjvm.so (unknown line)
unknown function (ip: 0x7f4c384acc7a)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a8822)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a8822)
unknown function (ip: 0x7f4c384a8822)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a8822)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a8822)
unknown function (ip: 0x7f4c384a8822)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a8822)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8822)
unknown function (ip: 0x7f4c384a83aa)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a86c9)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c384a8251)
unknown function (ip: 0x7f4c3849fcc8)
unknown function (ip: 0x7f4c56dd505e)
unknown function (ip: 0x7f4c573c9ec8)
unknown function (ip: 0x7f4c56e42d4d)
unknown function (ip: 0x7f4c57acd961)
unknown function (ip: 0x7f4c57acdb0c)
unknown function (ip: 0x7f4c57916926)
clone at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)

atexit hook threw an error: ErrorException("could not load symbol "jl_array_del_beg":
/usr/lib/jvm/java-1.16.0-openjdk-amd64/bin/java: undefined symbol: jl_array_del_beg")

I guess Julia4j does not work with julia 1.5.3? Or it is again something debian specific.

@subes
Copy link
Author

subes commented Dec 25, 2021

Ok, finally got it working by installing julia 1.7.1 from the website and using the following test:

public class Julia4JJNITest {
	static {
		try {
			System.load("/opt/julia/lib/libjulia.so");
			NativeUtils.loadLibraryFromJar(NativeUtils.libnameToPlatform("libjulia4j"));
		} catch (final IOException e) {
			e.printStackTrace();
		}
	}

	@Test
	public void juliaShouldWork() {
		Julia4J.jl_init();
		Julia4J.jl_eval_string("dump(:(1 + 2x^2))");
		Julia4J.jl_atexit_hook(0);
	}

}

@rssdev10
Copy link
Owner

CMAKE Julia might be changed - https://github.com/rssdev10/julia4j/blob/julia1.7_java17/swig/FindJULIA.cmake#L2
I copied it from https://github.com/JuliaInterop/libcxxwrap-julia/blob/main/FindJulia.cmake

In terms of variables, there is a check key cmake -LA .

The way with System.load("/opt/julia/lib/libjulia.so"); is not good. That should be happening automatically if julia libraries are visible.

Btw, check it with ldd libjulia4j.so
Also, I'm using ./gradlew test --stacktrace --info to check what is happening while jar is running.

And, I updated my local linux. Will try to reanimate it from my side at least for linux.

@subes
Copy link
Author

subes commented Dec 25, 2021

Ok, thanks for the info. I will work on the binding now since I am able to test. When you have a new version available that works without the System.load workaround, I will upgrade. Thanks!

@subes
Copy link
Author

subes commented Dec 25, 2021

Also can you please remove the System.out from the NativeUtils?

@rssdev10
Copy link
Owner

Also can you please remove the System.out from the NativeUtils?

Do it locally without a commit. I will remove it after check.

@subes
Copy link
Author

subes commented Dec 25, 2021

I made some progress: https://github.com/invesdwin/invesdwin-context-julia/blob/main/invesdwin-context-julia-parent/invesdwin-context-julia-runtime-julia4j/src/main/java/de/invesdwin/context/julia/runtime/julia4j/internal/JuliaEngineWrapper.java
Things I am missing:

  • A way to extract string results from julia
    • I am using a workaround to write json results to a output file, though reading strings from memory via JNI would be preferable
  • A way to properly handle errors
    • I tried using try/catch and writing errors to files, though this does not write parsing errors to the output file
    • even wrapping commands in eval(Meta.parse("varinfooo()")) does not output the error about the method not being found
    • the only response I get is that the return value of jl_eval_string might be null for error cases
    • I guess we need some more calls in Julia4j to be able to handle errors properly, like here: https://github.com/Non-Contradiction/JuliaCall/blob/master/src/JuliaCall.cpp image
  • After a few commands I get errors about illegal memory access

Here the output of the crash:

2021-12-25 15:56:28.001 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - > using InteractiveUtils; using Pkg; isinstalled(pkg::String) = any(x -> x.name == pkg && x.is_direct_dep, values(Pkg.dependencies())); if !isinstalled("JSON"); Pkg.add("JSON"); end; using JSON;; true
2021-12-25 15:56:28.374 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - < true
2021-12-25 15:56:28.423 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - > try; open("/tmp/invesdwin_temp_subes/825042@subes-lap/JuliaEngineWrapper.out", "w") do io; write(io, JSON.json(names(Main))) end; catch err; write(io, sprint(showerror, err, catch_backtrace())) end; true
2021-12-25 15:56:28.539 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - < true
2021-12-25 15:56:28.550 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - > hello = String("World"); true
2021-12-25 15:56:28.552 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - < true
2021-12-25 15:56:28.553 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - > world = "Hello " * hello * "!"; true
2021-12-25 15:56:28.554 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - < true
2021-12-25 15:56:28.554 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - > try; open("/tmp/invesdwin_temp_subes/825042@subes-lap/JuliaEngineWrapper.out", "w") do io; write(io, JSON.json(world)) end; catch err; write(io, sprint(showerror, err, catch_backtrace())) end; true
2021-12-25 15:56:28.592 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - < true
2021-12-25 15:56:28.593 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - > try; open("/tmp/invesdwin_temp_subes/825042@subes-lap/JuliaEngineWrapper.out", "w") do io; write(io, JSON.json(JSON.json(varinfo()))) end; catch err; write(io, sprint(showerror, err, catch_backtrace())) end; true
2021-12-25 15:56:29.035 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - < true
2021-12-25 15:56:29.037 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - > name = nothing; true
2021-12-25 15:56:29.038 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - < true
2021-12-25 15:56:29.038 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - > hello = nothing; true
2021-12-25 15:56:29.038 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - < true
2021-12-25 15:56:29.038 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - > world = nothing; true
2021-12-25 15:56:29.039 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - < true
2021-12-25 15:56:29.039 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - > try; open("/tmp/invesdwin_temp_subes/825042@subes-lap/JuliaEngineWrapper.out", "w") do io; write(io, JSON.json(JSON.json(varinfo()))) end; catch err; write(io, sprint(showerror, err, catch_backtrace())) end; true
2021-12-25 15:56:29.053 [ |main               ] DEBUG d.i.c.j.r.contract.IScriptTaskRunnerJulia.debug              - < true

signal (11): Speicherzugriffsfehler
in expression starting at none:0
unknown function (ip: 0x7f81b04b91c5)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a8822)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b049fcc8)
unknown function (ip: 0x7f81d021005e)
unknown function (ip: 0x7f81d07f1ce9)
JVM_InvokeMethod at /usr/lib/jvm/java-16-openjdk-amd64/lib/server/libjvm.so (unknown line)
unknown function (ip: 0x7f81b824b53f)
unknown function (ip: 0x7f81b14f8f33)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a8822)
unknown function (ip: 0x7f81b04a8822)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a8822)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a8822)
unknown function (ip: 0x7f81b04a8822)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a8822)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8822)
unknown function (ip: 0x7f81b04a83aa)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a86c9)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b04a8251)
unknown function (ip: 0x7f81b049fcc8)
unknown function (ip: 0x7f81d021005e)
unknown function (ip: 0x7f81d0804ec8)
unknown function (ip: 0x7f81d027dd4d)
unknown function (ip: 0x7f81d0f08961)
unknown function (ip: 0x7f81d0f08b0c)
unknown function (ip: 0x7f81d0d51926)
clone at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
Allocations: 1451647 (Pool: 1450899; Big: 748); GC: 2

@subes
Copy link
Author

subes commented Dec 25, 2021

Ok, the author of libjulia-clj documented a workaround to chain signal processing with julia: https://cnuernber.github.io/libjulia-clj/signals.html
When using the env variable: LD_PRELOAD=/usr/lib/jvm/default-java/lib/libjsig.so
The single threaded tests succeed. Though then the multithreaded tests fail (even though the threads use a lock to prevent concurrent access to julia4j):

signal (11): Speicherzugriffsfehler
in expression starting at none:0
Allocations: 16793536 (Pool: 16788827; Big: 4709); GC: 20
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f7fa7825a59, pid=1000120, tid=1000856
#
# JRE version: OpenJDK Runtime Environment (16.0.2+7) (build 16.0.2+7-Ubuntu-2)
# Java VM: OpenJDK 64-Bit Server VM (16.0.2+7-Ubuntu-2, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
# Problematic frame:
# C  [libjulia-internal.so.1+0x44a59]  jl_excstack_state+0x9
#
# Core dump will be written. Default location: Core dumps may be processed with "/usr/share/apport/apport %p %s %c %d %P %E" (or dumping to /opt/subes/dev/Entwicklung/invesdwin/invesdwin-oss/invesdwin-context-julia/invesdwin-context-julia-parent/invesdwin-context-julia-runtime-julia4j/core.1000120)
#
# An error report file with more information is saved as:
# /opt/subes/dev/Entwicklung/invesdwin/invesdwin-oss/invesdwin-context-julia/invesdwin-context-julia-parent/invesdwin-context-julia-runtime-julia4j/hs_err_pid1000120.log
Compiled method (c2)   16298 8437       4       ch.qos.logback.classic.Logger::callAppenders (47 bytes)
 total in heap  [0x00007f80f07f0690,0x00007f80f07f0e38] = 1960
 relocation     [0x00007f80f07f07f0,0x00007f80f07f0840] = 80
 main code      [0x00007f80f07f0840,0x00007f80f07f0ac0] = 640
 stub code      [0x00007f80f07f0ac0,0x00007f80f07f0af8] = 56
 oops           [0x00007f80f07f0af8,0x00007f80f07f0b00] = 8
 metadata       [0x00007f80f07f0b00,0x00007f80f07f0b60] = 96
 scopes data    [0x00007f80f07f0b60,0x00007f80f07f0cd8] = 376
 scopes pcs     [0x00007f80f07f0cd8,0x00007f80f07f0dc8] = 240
 dependencies   [0x00007f80f07f0dc8,0x00007f80f07f0de0] = 24
 handler table  [0x00007f80f07f0de0,0x00007f80f07f0e10] = 48
 nul chk table  [0x00007f80f07f0e10,0x00007f80f07f0e38] = 40
Compiled method (c1)   16299 8111   !   3       de.invesdwin.util.lang.description.internal.TextDescriptionFormatter::safeObjectAppend (54 bytes)
 total in heap  [0x00007f80e9aeec10,0x00007f80e9aefd78] = 4456
 relocation     [0x00007f80e9aeed70,0x00007f80e9aeeec0] = 336
 main code      [0x00007f80e9aeeec0,0x00007f80e9aef820] = 2400
 stub code      [0x00007f80e9aef820,0x00007f80e9aef910] = 240
 oops           [0x00007f80e9aef910,0x00007f80e9aef930] = 32
 metadata       [0x00007f80e9aef930,0x00007f80e9aef970] = 64
 scopes data    [0x00007f80e9aef970,0x00007f80e9aefaf0] = 384
 scopes pcs     [0x00007f80e9aefaf0,0x00007f80e9aefcd0] = 480
 dependencies   [0x00007f80e9aefcd0,0x00007f80e9aefce8] = 24
 handler table  [0x00007f80e9aefce8,0x00007f80e9aefd48] = 96
 nul chk table  [0x00007f80e9aefd48,0x00007f80e9aefd78] = 48

signal (6): Abgebrochen
in expression starting at none:0
Allocations: 16793536 (Pool: 16788827; Big: 4709); GC: 20
#
# If you would like to submit a bug report, please visit:
#   Unknown
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

It seems Julia4j requires binding to a specific java thread similar to JEP for python. Though JEP supports multiple threads/contexts while Julia4j only allows single threaded access to Julia. Julia itself it able to execute commands multithreaded though while python only allows one thread per context.

The workaround I implemented is to call Julia4j always from the same thread. This makes all testcases green: https://github.com/invesdwin/invesdwin-context-julia/blob/main/invesdwin-context-julia-parent/invesdwin-context-julia-runtime-julia4j/src/test/java/de/invesdwin/context/julia/runtime/julia4j/Julia4jScriptTaskRunnerJuliaTest.java

It would still be great if the error handling could be improved.

@rssdev10
Copy link
Owner

Also can you please remove the System.out from the NativeUtils?

I tried to find a way how to force Java to do self search of dependencies. I changed a way to collect set of paths into java.library.path and replaced System.out by extended exception message. But looks, newest Java version don't allow to setup it dynamically - https://stackoverflow.com/questions/15409223/adding-new-paths-for-native-libraries-at-runtime-in-java .
One declared way I found - https://stackoverflow.com/questions/5425034/java-load-shared-libraries-with-dependencies but that requires to do some changes with SWIG. And not sure that the solution is a cross platform - https://github.com/victor-paltz/global-load-library with dlopen(pathCharPointer, RTLD_LAZY | RTLD_GLOBAL).

I can run the test with LD_LIBRARY_PATH=/usr/lib64/jvm/java-11-openjdk-11/lib:/usr/lib64/jvm/java-11-openjdk-11/lib/server:~/projects/julia/julia-1.7.1/lib ./gradlew test --stacktrace --info and moreover, I see that the tests are working properly.

@rssdev10
Copy link
Owner

For getting String values back from Julia, looks like we need to have an appropriate low level function to unpack. As an additional function from SWIG.

See existing code with a wrapper NewStringUTF

SWIGEXPORT jstring JNICALL Java_org_julia_jni_swig_Julia4JJNI_jl_1get_1default_1sysimg_1path(JNIEnv *jenv, jclass jcls) {
  jstring jresult = 0 ;
  char *result = 0 ;
  
  (void)jenv;
  (void)jcls;
  result = (char *)jl_get_default_sysimg_path();
  if (result) jresult = (*jenv)->NewStringUTF(jenv, (const char *)result);
  return jresult;
}

But in case of getting a value we have a unsafe pointer but not the String.

        engine.put("x", "hello");
        // print global variable "x"
        engine.eval("res = join(fill(x, 10), \";\");");
        Object result = engine.get("res");

        SWIGTYPE_p_void bytes = Julia4J.jl_unbox_voidpointer((SWIGTYPE_p_jl_value_t) result);

That I didn't check under debugger yet.

@subes
Copy link
Author

subes commented Dec 25, 2021

Extracting something from an unsafe pointer does not work. It crashes the JVM. I tried it with an UnsafeBuffer:

final String command = "__ans__ = JSON.json(" + variable + ")";
IScriptTaskRunnerJulia.LOG.debug("> get %s", variable);
final SWIGTYPE_p_jl_value_t value = Julia4J.jl_eval_string(command);
try {
   assertResponseNotNull(variable, value);
   final SWIGTYPE_p_void pointer = Julia4J.jl_unbox_voidpointer(value);
   final long address = SwigAccessor.getPointer(pointer);
   final IByteBuffer buffer = ByteBuffers.wrap(address, MAX_STRING_LENGTH);
   final StringBuilder sb = new StringBuilder();
   for (int i = 0; i < buffer.capacity(); i++) {
       final char c = buffer.getChar(i); // CRASHES HERE AT INDEX 0
       if (c == '\0') {
           break;
       } else {
           sb.append(c);
       }
   }

@rssdev10
Copy link
Owner

rssdev10 commented Dec 25, 2021

Check that branch please - https://github.com/rssdev10/julia4j/tree/jl_string_wrapper

I added a simple wrapper:

 char * jl_unbox_charpointer(jl_value_t *v) {
     return (char *)jl_unbox_voidpointer(v);
 }

And added appropriate test for that - https://github.com/rssdev10/julia4j/blob/jl_string_wrapper/src/test/java/Julia4JScriptingTest.java#L89

But not able to activate due to issues with libopenlibm.so

@subes
Copy link
Author

subes commented Dec 25, 2021

I guess with "activate" you mean not compiling into the libjulia4j.so. I guess that is the reason why I get the following error:
/usr/lib/jvm/java-1.16.0-openjdk-amd64/bin/java: symbol lookup error: /tmp/nativeutils130077007066/libjulia4j.so: undefined symbol: jl_unbox_charpointer

@rssdev10
Copy link
Owner

That should be there.
nm -D libjulia4j.so

0000000000207270 B __bss_start
                 w __cxa_finalize
0000000000207270 D _edata
0000000000207278 B _end
                 w __gmon_start__
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
...
0000000000005eb4 T Java_org_julia_jni_swig_Julia4JJNI_jl_1unbox_1charpointer
...
0000000000005df6 T Java_org_julia_jni_swig_Julia4JJNI_jl_1unbox_1voidpointer
...
                 U jl_unbox_charpointer
...
                 U jl_unbox_voidpointer

@rssdev10
Copy link
Owner

Fixed that part and pushed the commit. The function body was absent despite export table. But that was not a proper way to get the result.

signal (11): segmentation fault
in expression starting at none:0
jl_unbox_voidpointer at /buildworker/worker/package_linux64/build/src/datatype.c:1109
Java_org_julia_jni_swig_Julia4JJNI_jl_1unbox_1charpointer at /tmp/nativeutils4949978148803/libjulia4j.so (unknown line)

jl_unbox_voidpointer cannot process string value.

@subes
Copy link
Author

subes commented Dec 28, 2021

Would be great if we can fix the string handling in julia4j.

I did some performance tests. Currently libjulia-clj is the fastest integration:

Though I also created an integration for libpython-clj to compare that against a JNI integration with JEP. libpython-clj is a lot slower than JEP:

By that rationale: when the strings are transported via memory instead of files, julia4j (also JNI) could outperform libjulia-clj by a lot.

@subes
Copy link
Author

subes commented Dec 30, 2021

I found a workaround for the error handling by using functions that wrap the executions by using try/catch and redirect_stderr. Reusing the functions also makes it rather fast because the evaluation wrappers don't have to be recompiled all the time by Julia.
See workarounds here: https://github.com/invesdwin/invesdwin-context-julia/blob/master/invesdwin-context-julia-parent/invesdwin-context-julia-runtime-julia4j/src/main/java/de/invesdwin/context/julia/runtime/julia4j/internal/UnsafeJuliaEngineWrapper.java

        evalUnchecked("function j4j_exec(cmd) open(\"" + outputFilePath
                + "\", \"w\") do io; redirect_stderr(io) do; try eval(Meta.parse(cmd)) catch err @error err; showerror(io, err); end; end; end; end");
        evalUnchecked("function j4j_get(cmd) open(\"" + outputFilePath
                + "\", \"w\") do io; redirect_stderr(io) do; try __ans__ = eval(Meta.parse(cmd)); write(io, JSON.json(__ans__)); catch err @error err; showerror(io, err); end; end; end; end;");

Thus we don't need additonal exception handling functions in julia4j.

When it becomes possible to return strings in memory, it will be simple to redirect the stderr/catch output into a string to be returned as an error response. That is also the only thing missing that I currently see.

With the current state the performance has improved a lot (due to the precompiled functions):
image

@rssdev10
Copy link
Owner

rssdev10 commented Jan 6, 2022

Hi, I checked the code and found solution how to get variables of any type. And also, added unpack into the String type.
Actually, we have Julia embedded into a current address space. We can call eval multiple times but not in parallel. eval returns a result of expression. So, for a variable name as expression that returns an actual value with (jl_value_t*) type. jl_unbox_string is new wrapper for exact Julia-string.

For now the code looks like:

        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("julia");

        engine.put("x", "hello");
        // print global variable "x"
        engine.eval("res = join(fill(x, 10), \";\");");

        SWIGTYPE_p_jl_value_t result = (SWIGTYPE_p_jl_value_t)engine.eval("res");
        Julia4J.jl_show(result);

        String str = Julia4J.jl_unbox_string((SWIGTYPE_p_jl_value_t) result);
        assertEquals(String.join(";", Collections.nCopies(10, "hello")), str);

And, some wrapper around engine.eval and further jl_unbox_... might be implemented.

The branch is still https://github.com/rssdev10/julia4j/tree/jl_string_wrapper

@subes
Copy link
Author

subes commented Jan 7, 2022

Unwrapping the string response works great: subes/archive_invesdwin-context-julia@0a227bf

It does not seem to make a difference in performance after all:
grafik

This is the performance with disabled stdout:
evalUnchecked("redirect_stdout(open(\"/dev/null\", \"w\"))");
grafik

This is the performance of libjulia-clj:
grafik

  • note the slower startup because it compiles the clojure code for a few seconds (compared to previous times)
  • though the parallel test seems to be similarly fast (though I did not disable stdout there)

Though it is still great that we were able to get rid of the output file. I think we can close this issue.

@rssdev10
Copy link
Owner

rssdev10 commented Jan 7, 2022

Absence of time difference looks weird. But anyway the most valuable issue is absence of centralized building of binaries for different OS and java/julia versions.

#3 and #4 has been merged.

Closing this issue.

@rssdev10 rssdev10 closed this as completed Jan 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants