简体   繁体   English

JNA / BridJ等中的分段错误

[英]Segmentation faults in JNA/BridJ etc

I've rebuilt my Java/C++ project several times using JNI, JNA, BridJ, and JavaCPP, and every time I encounter random (unpredictable) segmentation faults. 我已经使用JNI,JNA,BridJ和JavaCPP多次重建了Java / C ++项目,每次遇到随机(不可预测的)分段错误时,我都会对其进行重建。 I have verified that a pure-C++ executable using this library never causes segmentation faults, and in the case of BridJ, narrowed it down to Java's garbage collector by explicitly invoking it. 我已经验证了使用此库的纯C ++可执行文件不会引起分段错误,对于BridJ,通过显式调用将其范围缩小到Java的垃圾收集器。

One thought was that these libraries are creating Java-side pointer objects that call free or delete when they get garbage collected (through finalize ) instead of treating the pointers that C++ returns as borrowed references, as they should in this application. 一种想法是,这些库正在创建Java端指针对象,当它们被垃圾回收时(通过finalize )调用freedelete ,而不是像在本应用程序中那样将C ++返回的指针视为借用引用。

But I've tried one additional test (not represented below): I turned every pointer in the API into an int64_t ( long in Java) and explicitly cast as the appropriate type in C++. 但是我尝试了另一项测试(以下未显示):我将API中的每个指针都转换为int64_t (Java中为long ),并在C ++中显式转换为适当的类型。 I still see the rare segfaults. 我仍然看到罕见的段错误。

So my question is broad: what could be causing this issue? 所以我的问题很广泛:什么可能导致此问题? I'll accept answers using JNA or BridJ because I can easily switch between these two. 我将使用JNA或BridJ接受答案,因为我可以轻松地在这两个之间切换。 I think I'm missing a fundamental issue because this problem is so pervasive in all the libraries I've tried. 我想我遗漏了一个基本问题,因为这个问题在我尝试过的所有库中如此普遍。

For concreteness, here's my JNA version. 具体来说,这是我的JNA版本。 I'm linking against the CERN ROOT library. 我正在链接CERN ROOT库。 RootTreeReader.h is: RootTreeReader.h是:

#ifndef ROOTTREEREADER_H
#define ROOTTREEREADER_H

#include <TFile.h>
#include <TTreeReader.h>
#include <TTreeReaderValue.h>
#include <TTreeReaderArray.h>

using namespace ROOT::Internal;

extern "C" {
  TFile *newFile(const char *fileLocation);
  TTreeReader *newReader(TFile *file, const char *treeLocation);
  bool readerNext(TTreeReader *reader);

  TTreeReaderValueBase *newValue_float(TTreeReader *reader, const char *name);

  float getValue_float(TTreeReaderValueBase *value);
}

#endif // ROOTTREEREADER_H

RootTreeReader.cpp is: RootTreeReader.cpp是:

#include <string>

#include "RootTreeReader.h"

TFile *newFile(const char *fileLocation) {
  return TFile::Open(fileLocation);
}

TTreeReader *newReader(TFile *file, const char *treeLocation) {
  return new TTreeReader(treeLocation, file);
}

bool readerNext(TTreeReader *reader) {
  return reader->Next();
}

TTreeReaderValueBase *newValue_float(TTreeReader *reader, const char *name) {
  return new TTreeReaderValue<float>(*reader, name);
}

float getValue_float(TTreeReaderValueBase *value) {
  return *((float*)value->GetAddress());
}

Their Makefile is 他们的Makefile是

all: RootTreeReader.cpp
    mkdir -p ../../../target/native/linux-x86-64
    g++ RootTreeReader.cpp -o ../../../target/native/linux-x86-64/libRootTreeReader.so \
        -fPIC -shared \
        -Wl,--no-as-needed $(shell root-config --cflags --ldflags --libs) -lTreePlayer

JNAerator generates the following RootTreeReaderLibrary.java: JNAerator生成以下RootTreeReaderLibrary.java:

package org.dianahep.scaroot;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
import com.sun.jna.PointerType;
/**
 * JNA Wrapper for library <b>RootTreeReader</b><br>
 * This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<br>
 * a tool written by <a href="http://ochafik.com/">Olivier Chafik</a> that <a href="http://code.google.com/p/jnaerator/wiki/CreditsAndLicense">uses a few opensource projects.</a>.<br>
 * For help, please visit <a href="http://nativelibs4java.googlecode.com/">NativeLibs4Java</a> , <a href="http://rococoa.dev.java.net/">Rococoa</a>, or <a href="http://jna.dev.java.net/">JNA</a>.
 */
public interface RootTreeReaderLibrary extends Library {
    public static final String JNA_LIBRARY_NAME = "RootTreeReader";
    public static final NativeLibrary JNA_NATIVE_LIB = NativeLibrary.getInstance(RootTreeReaderLibrary.JNA_LIBRARY_NAME);
    public static final RootTreeReaderLibrary INSTANCE = (RootTreeReaderLibrary)Native.loadLibrary(RootTreeReaderLibrary.JNA_LIBRARY_NAME, RootTreeReaderLibrary.class);
    /**
     * Original signature : <code>TFile* newFile(const char*)</code><br>
     * <i>native declaration : src/main/cpp/RootTreeReader.h:5</i><br>
     * @deprecated use the safer methods {@link #newFile(java.lang.String)} and {@link #newFile(com.sun.jna.Pointer)} instead
     */
    @Deprecated 
    RootTreeReaderLibrary.TFile newFile(Pointer fileLocation);
    /**
     * Original signature : <code>TFile* newFile(const char*)</code><br>
     * <i>native declaration : src/main/cpp/RootTreeReader.h:5</i>
     */
    RootTreeReaderLibrary.TFile newFile(String fileLocation);
    /**
     * Original signature : <code>TTreeReader* newReader(TFile*, const char*)</code><br>
     * <i>native declaration : src/main/cpp/RootTreeReader.h:6</i><br>
     * @deprecated use the safer methods {@link #newReader(org.dianahep.scaroot.RootTreeReaderLibrary.TFile, java.lang.String)} and {@link #newReader(org.dianahep.scaroot.RootTreeReaderLibrary.TFile, com.sun.jna.Pointer)} instead
     */
    @Deprecated 
    RootTreeReaderLibrary.TTreeReader newReader(RootTreeReaderLibrary.TFile file, Pointer treeLocation);
    /**
     * Original signature : <code>TTreeReader* newReader(TFile*, const char*)</code><br>
     * <i>native declaration : src/main/cpp/RootTreeReader.h:6</i>
     */
    RootTreeReaderLibrary.TTreeReader newReader(RootTreeReaderLibrary.TFile file, String treeLocation);
    /**
     * Original signature : <code>bool readerNext(TTreeReader*)</code><br>
     * <i>native declaration : src/main/cpp/RootTreeReader.h:7</i>
     */
    byte readerNext(RootTreeReaderLibrary.TTreeReader reader);
    /**
     * Original signature : <code>TTreeReaderValueBase* newValue_float(TTreeReader*, const char*)</code><br>
     * <i>native declaration : src/main/cpp/RootTreeReader.h:9</i><br>
     * @deprecated use the safer methods {@link #newValue_float(org.dianahep.scaroot.RootTreeReaderLibrary.TTreeReader, java.lang.String)} and {@link #newValue_float(org.dianahep.scaroot.RootTreeReaderLibrary.TTreeReader, com.sun.jna.Pointer)} instead
     */
    @Deprecated 
    RootTreeReaderLibrary.TTreeReaderValueBase newValue_float(RootTreeReaderLibrary.TTreeReader reader, Pointer name);
    /**
     * Original signature : <code>TTreeReaderValueBase* newValue_float(TTreeReader*, const char*)</code><br>
     * <i>native declaration : src/main/cpp/RootTreeReader.h:9</i>
     */
    RootTreeReaderLibrary.TTreeReaderValueBase newValue_float(RootTreeReaderLibrary.TTreeReader reader, String name);
    /**
     * Original signature : <code>float getValue_float(TTreeReaderValueBase*)</code><br>
     * <i>native declaration : src/main/cpp/RootTreeReader.h:11</i>
     */
    float getValue_float(RootTreeReaderLibrary.TTreeReaderValueBase value);
    public static class TFile extends PointerType {
        public TFile(Pointer address) {
            super(address);
        }
        public TFile() {
            super();
        }
    };
    public static class TTreeReader extends PointerType {
        public TTreeReader(Pointer address) {
            super(address);
        }
        public TTreeReader() {
            super();
        }
    };
    public static class TTreeReaderValueBase extends PointerType {
        public TTreeReaderValueBase(Pointer address) {
            super(address);
        }
        public TTreeReaderValueBase() {
            super();
        }
    };
}

and I call it like this: 我这样称呼它:

Native.setProtected(true)
println("Native.isProtected", Native.isProtected)   // it's true on Linux; I've tried this with and without

val lib = RootTreeReaderLibrary.INSTANCE

println("one")
val file = lib.newFile("TrackResonanceNtuple.root")
println("two")
val reader = lib.newReader(file, "TrackResonanceNtuple/twoMuon")
println("three")
val mass = lib.newValue_float(reader, "mass_mumu")
println("four")
var counter = 0
while (lib.readerNext(reader) > 0) {
  val num = lib.getValue_float(mass)
  println(num)
  counter += 1
  // if (counter % 1000 == 0) {
  //   println("gc start")
  //   System.gc()
  //   println("gc end")
  // }
}
println("five")

It segfaults rarely with or without explicit garbage collector invocations. 无论是否有明确的垃圾收集器调用,它都很少发生段故障。 A BridJ version of this segfaults rarely without and frequently with explicit garbage collector invocations. 这种段错误的BridJ版本很少没有,并且经常使用显式垃圾收集器调用。

The quick fix: 快速修复:

export LD_PRELOAD=/path/to/libjsig.so

The problem is that CERN ROOT tries to install handlers for the same signals as the JDK. 问题在于,CERN ROOT试图为与JDK相同的信号安装处理程序。 Oracle recommends to preload libjsig.so , which provides a "signal-chaining facility", to work around these kinds of issues: Oracle建议预加载libjsig.so (它提供“信号链接工具”)来解决以下问题:

https://docs.oracle.com/javase/6/docs/technotes/guides/vm/signal-chaining.html https://docs.oracle.com/javase/6/docs/technotes/guides/vm/signal-chaining.html

Edit from Jim: 从吉姆编辑:

Specifically for the ROOT framework, you can turn off signal handling by calling 专门针对ROOT框架,您可以通过调用来关闭信号处理

gSystem->ResetSignals();

The discussion can be found on this ROOT message board: https://root.cern.ch/phpBB3/viewtopic.php?t=8231 可以在此ROOT留言板上找到讨论: https : //root.cern.ch/phpBB3/viewtopic.php? t =8231

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM