1

I develop an app in java that loads a C++ shared library using JNA. The detailed procedure is the following.

  1. Find library in the jar
  2. use System.load( C_LIBRARY_PATH ) with the result of step 1
  3. Load library using JNA tools: Wrapper INSTANCE = Native.loadLibrary( C_LIBRARY_PATH, Wrapper.class );

This procedure is used to create a wrapper of the c++ library and hence produces a java library that is used for other projects. Point 1 and 2 are inspired by the work of adamheinrich. The existence of the file is checked before calling the load function.

When using the java wrapper in a project I randomly have an error like below. Could you guide me in debugging and try to control this random failure?

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007ff754e1cf9b, pid=8922, tid=9159
#
# JRE version: OpenJDK Runtime Environment (10.0.1+10) (build 10.0.1+10)
# Java VM: OpenJDK 64-Bit Server VM (10.0.1+10, mixed mode, tiered, compressed oops, g1 gc, linux-amd64)
# Problematic frame:
# C  [libc.so.6+0x15cf9b]  __memmove_avx_unaligned_erms+0x8b
#
# Core dump will be written. Default location: Core dumps may be processed with "/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h %e" (or dumping to /home/core.8922)
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
---------------  S U M M A R Y ------------

Command Line: -Drebel.base=/home/.jrebel -Drebel.env.ide.plugin.version=2018.1.2 -Drebel.env.ide.version=2018.1 -Drebel.env.ide.product=IU -Drebel.env.ide=intellij -Drebel.notification.url=http://localhost:17434 -agentpath:/home/.IntelliJIdea2018.1/config/plugins/jr-ide-idea/lib/jrebel6/lib/libjrebel64.so -Dvisualvm.id=1716948864493 -Dmaven.multiModuleProjectDirectory=/home/ -Dmaven.home=/home/Documents/ideaIU-2018.1/idea-IU-181.4203.550/plugins/maven/lib/maven3 -Dclassworlds.conf=/home/Documents/ideaIU-2018.1/idea-IU-181.4203.550/plugins/maven/lib/maven3/bin/m2.conf -javaagent:/home/ideaIU-2018.1/idea-IU-181.4203.550/lib/idea_rt.jar=36953:/home/ideaIU-2018.1/idea-IU-181.4203.550/bin -Dfile.encoding=UTF-8 org.codehaus.classworlds.Launcher -Didea.version=2018.1 -P tomcat clean tomcat7:run -f pom.xml

Host: Intel(R) Core(TM) i7-6560U CPU @ 2.20GHz, 4 cores, 15G, Fedora release 28 (Twenty Eight)
Time: Wed Jun 27 08:35:16 2018 CEST elapsed time: 140 seconds (0d 0h 2m 20s)

---------------  T H R E A D  ---------------

Current thread (0x00007ff74efc1000):  JavaThread "http-bio-8080-exec-7" daemon [_thread_in_native, id=9159, stack(0x00007ff694588000,0x00007ff694689000)]

Stack: [0x00007ff694588000,0x00007ff694689000],  sp=0x00007ff694683ae8,  free space=1006k
Native frames: (J=compiled Java code, A=aot compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [libc.so.6+0x15cf9b]  __memmove_avx_unaligned_erms+0x8b

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  java.lang.ClassLoader$NativeLibrary.load0(Ljava/lang/String;Z)Z+0 java.base@10.0.1
j  java.lang.ClassLoader$NativeLibrary.load()Z+63 java.base@10.0.1
j  java.lang.ClassLoader$NativeLibrary.loadLibrary(Ljava/lang/Class;Ljava/lang/String;Z)Z+239 java.base@10.0.1
j  java.lang.ClassLoader.loadLibrary0(Ljava/lang/Class;Ljava/io/File;)Z+46 java.base@10.0.1
j  java.lang.ClassLoader.loadLibrary(Ljava/lang/Class;Ljava/lang/String;Z)V+50 java.base@10.0.1
j  java.lang.Runtime.load0(Ljava/lang/Class;Ljava/lang/String;)V+77 java.base@10.0.1
j  java.lang.System.load(Ljava/lang/String;)V+7 java.base@10.0.1
j  *hiddenPackageName*.NativeUtils.loadLibraryFromJar(Ljava/lang/String;Ljava/lang/Class;)V+359
j  *hiddenPackageName*.LibraryInitializer.<clinit>()V+13
v  ~StubRoutines::call_stub

In order to give full overview of the issue I copy the code of points 1 and 2 of the procedure below:

public static <T> void loadLibraryFromJar(String path, Class<T> clazz) throws IOException {
    if( !path.startsWith( "/" ) ){
        throw new IllegalArgumentException( "The path has to be absolute (start with '/')." );
    }

    // Obtain filename from path
    String[] parts = path.split( "/" );
    String filename = (parts.length > 1) ? parts[parts.length - 1] : null;

    // Split filename to prefix and suffix (extension)
    String prefix = "";
    String suffix = null;
    if( filename != null ){
        parts = filename.split( "\\.", 2 );
        prefix = parts[0];
        suffix = (parts.length > 1) ? "." + parts[parts.length - 1] : null; // Thanks, davs! :-)
    }

    // Check if the filename is okay
    if( filename == null || prefix.length() < 3 ){
        throw new IllegalArgumentException( "The filename has to be at least 3 characters long." );
    }

    // Prepare temporary file
    File temp = File.createTempFile( prefix, suffix );
    temp.deleteOnExit();

    if( !temp.exists() ){
        throw new FileNotFoundException( "File " + temp.getAbsolutePath() + " does not exist." );
    }

    // Prepare buffer for data copying
    byte[] buffer = new byte[1024];
    int readBytes;

    // Open and check input stream
    try( InputStream is = clazz.getResourceAsStream( path ) ){
        if( is == null ){
            throw new FileNotFoundException( "File " + path + " was not found inside JAR." );
        }

        // Open output stream and copy data between source file in JAR and the temporary file
        try( OutputStream os = new FileOutputStream( temp ) ){
            while( (readBytes = is.read( buffer )) != -1 ){
                os.write( buffer, 0, readBytes );
            }
        }
    }

    // Finally, load the library
    System.load( temp.getAbsolutePath() );
}

Edit: The value of temp.getAbsolutePath() is subsequently equal to:

/tmp/libgcc_s7394566251608466486.so.1

/tmp/libgomp14105897271936182307.so.1

/tmp/libpthread18366551914953688272.so.0

/tmp/libstdc++3062153806840733749.so.6

/tmp/libfitting1621020429668741777.so.1.0
Yvus
  • 338
  • 1
  • 20
  • [**__memcpy_sse2_unaligned - what does this mean in detail?**](https://stackoverflow.com/q/32526042/996081) - Suggests that something goes wrong in a `memcpy`. Could be that the native code emitted by the JITer encounters a null pointer in the `System.load` call. – cbr Jun 27 '18 at 11:54
  • What is the value of `temp.getAbsolutePath()` passed to `System.load()`? – cbr Jun 27 '18 at 11:57
  • @cubrr: I have edited the question to contain your request. Note that I think now that the error cmoes from step 3 of the process: Native.loadLibrary. – Yvus Jun 27 '18 at 12:21
  • I'm guessing that `INSTANCE` is an implicitly static variable inside your `Wrapper` interface. This `Native.loadLibrary` call is made when the `Wrapper` class is first referenced (I believe). I wonder if the `System.load` call is done on a different thread than the class initialization? If so, there could be a race condition at play - could JNA attempt to load the library while it's still being written to or loaded? Instead of using a singleton-ish static `INSTANCE`, try loading an instance manually when you need it and pass that instance around. See if the error still occurs then. – cbr Jun 27 '18 at 14:03
  • @cubrr: you are guessing right, it is an implicitly static variable inside the interface. Changing this is a huge work since the entire concept is that this INSTANCE is available to all instantiation of the classes. This is very useful. It has never proven to be a problem until we used this library inside a web app... I will think of a way to check if there is a thread competition. and keep you posted. Thanks a lot. – Yvus Jun 27 '18 at 15:24
  • You could put a breakpoint at where you call `System.load`, check the thread ID, then also perhaps breakpoint `Native.loadLibrary` itself and see the thread ID when it's loading `C_LIBRARY_PATH`. – cbr Jun 27 '18 at 15:47
  • @cubrr: thanks for your time and advises, it lead me to the answer that I just posted. Let me know if the answer is clear or edit if needed. Thanks – Yvus Jun 28 '18 at 12:52

1 Answers1

1

I was accidentally loading twice the same library in multiple java classes. More precisely, the call Wrapper INSTANCE = Native.loadLibrary( C_LIBRARY_PATH, Wrapper.class ); was done in a nested interface that I replicated in all the different objects that were using the same c++ library. This, as reminded by @cubrr, is done statically at instantiation of the first object. Hence the library was loaded multiple times. Below a speudo-code illustrating my mistake.

class A {
    protected interface Wrapper extends Library {
        Wrapper INSTANCE = Native.loadLibrary( C_LIBRARY_PATH, Wrapper.class ); 
        double functionA();
    }
    public double callA() {
        return Wrapper.INSTANCE.functionA();
    }
}

class B {
    protected interface Wrapper extends Library {
        Wrapper INSTANCE = Native.loadLibrary( C_LIBRARY_PATH, Wrapper.class ); 
        double functionB();
    }
    public double callB() {
        return Wrapper.INSTANCE.functionB();
    }
}

which should be done like so

class Parent {
    protected interface Wrapper extends Library {
        Wrapper INSTANCE = Native.loadLibrary( C_LIBRARY_PATH, Wrapper.class ); 
        double functionA();
        double functionB();
    }
}

class A extends Parent {
    public double callA() {
        return Wrapper.INSTANCE.functionA();
    }
}

class B extends Parent {
    public double callB() {
        return Wrapper.INSTANCE.functionB();
    }
}
Yvus
  • 338
  • 1
  • 20
  • Wow, that's interesting. I would've thought that JNA deals with attempting to re-load an already loaded library. Great to hear I was of some use! – cbr Jun 28 '18 at 12:57
  • By the way, do accept your answer since it provides and answer to your question! – cbr Jun 28 '18 at 15:11
  • I have to wait 16 hours more to do so :-) – Yvus Jun 28 '18 at 15:21
  • This seems kind of convoluted. I'd expect you to define a single "global" instance of the native library mapping and then refer to that one in each of your classes. The only reason I'd bury instantiation in a class is if I _wanted_ separate instantiations of the library (e.g. fortran code with shared data blocks that I want to run multithreaded). – technomage Jun 28 '18 at 15:23
  • I wished that the java users is not bothered (or even aware) that there is a c++ library behind the class he is using. I thought that would do the trick. If you have a global attribute that is passed around, then it overloads the usage for no reason. The A class is a wrapper of a subpart of the c++ library. The B class is a wrapper of an other subpart of the c++ library. The users does not need to manage the native code. its automatic. Like python is using c++ compiled code we do not notice... Let me know what you think? – Yvus Jun 28 '18 at 15:35
  • I'd move all native interfaces together to a package. – cbr Jun 28 '18 at 16:16
  • Which is what is done. I maybe miss understood the point that was made by @technomage. is he simply meaning that the `INSTANCE` should be instantiated as static in the Parent class outside the Wrapper interface? Which to me would make not much differences. Could you edit the answer (or do an other one) in order that I clearly understand your point? How would you manage the `INSTANCE` object? – Yvus Jun 29 '18 at 06:48