Код JNI не может вызвать функцию C на Linux, но не Windows - PullRequest
2 голосов
/ 21 февраля 2020

У меня следующий Java код, сгенерированный с помощью SWIG. Хотя SWIG использовался для генерации кода, знание SWIG не требуется для понимания этого вопроса

Код пользователя Java:

public class UuidUtil implements OCUuidUtilConstants {
  public static Uuid generateUuid() {
    long cPtr = UuidUtilJNI.generateUuid();
    return (cPtr == 0) ? null : new Uuid(cPtr, true);
  }
}

UuidUtilJNI.generateUuid() определяется следующим образом

Java код вызова кода JNI

public class UuidUtilJNI {
  public final static native long generateUuid();
}

SWIG-генерируемый код оболочки JNI C uuid_wrap.c

uuid_t * jni_gen_uuid()
{
  printf("Inside jni_gen_uuid\n");
  uuid_t *value = (uuid_t *)malloc(sizeof(uuid_t));
  printf("Calling gen_uuid\n");
  gen_uuid(value);
  return value;
}

SWIGEXPORT jlong JNICALL Java_UuidUtilJNI_generateUuid(JNIEnv *jenv, jclass jcls) {
  jlong jresult = 0 ;
  uuid_t *result = 0 ;

  (void)jenv;
  (void)jcls;
  result = (uuid_t *)jni_gen_uuid();
  *(uuid_t **)&jresult = result; 
  return jresult;
}

частичный C код для gen_uuid(uuid_t *uuid) function uuid.c

void gen_uuid(uuid_t *uuid)
{
  printf("Inside gen_uuid\n");
  /* code to set the uuid to type 4 UUID according to RFC4122 */
}

Я тестирую его следующим образом:

@Test
public void generateAndConvert() {
    Uuid testUuid = UuidUtil.generateUuid();
    assertNotNull(testUuid);
    // other test code left out for readability
}

Когда тест выполняется на Windows, код работает как положено.

Когда я пытаюсь запустить тот же код на Linux, тест зависает.

Он печатает 2 из 3 найденных в коде строк печати. ​​

Inside jni_gen_uuid
Calling gen_uuid

строка Inside gen_uuid никогда не вызывается при запуске теста на компьютере Linux (Fedora 30).

Сначала я подумал, что в выводе объектных файлов было какое-то несоответствие, что приводило к сбою кода оболочки, когда вызывая код UUID. Я сравнил флаги сборки, используемые для построения кода uuid.c и uuid_wrap.c, и они используют те же флаги, за исключением того, что некоторые предупреждения о сборке отключены для кода оболочки, поскольку он генерируется инструментом и не предназначен для быть измененным мной.

Я действительно не знаю, где еще искать.

Я проверил файл uuid.o, используя nm и objdump, насколько я могу сказать, что он имеет символ gen_uuid .

Мне не удалось правильно подключить gdb к работающему образцу, поэтому я не уверен, что это даст какую-либо полезную информацию.

Есть предложения, которые могут помочь найти проблему?

1 Ответ

0 голосов
/ 29 апреля 2020

К сожалению, это решение очень точно указывало c на мой код.

Когда я писал, что оператор print

printf("Inside gen_uuid\n");
/* code to set the uuid to type 4 UUID according to RFC4122 */ 

никогда не вызывался, я ошибался. Текст был просто не спущен на экран. Добавление fflush(stdout); после того, как оператор print показал бы, что действительно вызывается функция gen_uuid.

Как отмечено в этом вопросе, этот код генерирует UUID типа 4, который является случайным UUID.

Код, генерирующий случайные числа, задается ОС c. Перед считыванием случайного числа генератор случайных чисел должен быть инициализирован. На Linux это делается путем захвата файлового дескриптора к /dev/urandom на Windows, это делается с помощью функции srand().

Linux будет вызывать ошибку при генерации случайного числа, если random_init не был назван. Windows с другой стороны выдаст номер, даже если он не будет засеян.

Решением было позвонить randomInit() до вызова generateUuid() в модульном тесте.

Если вы если разработчик исследует аналогичную ошибку и использует отладку printf, чтобы проверить, была ли достигнута строка кода, то убедитесь, что оператор print сброшен на экран, прежде чем сделать вывод, что строка кода не достигнута.

...