2

I am attempting to map the kstat library in Solaris 11.3 to Java using JNA. While I've managed to get most of the structures working, I've spent the last 24 hours fighting with a particularly difficult union-within-a-structure-within-a-union.

I am successfully retrieving a pointer to a kstat_named structure I need using kstat_data_lookup(). My code properly retrieves most of the data (name, data_type, and non-struct members of the union) in this C structure:

typedef struct kstat_named {
   char    name[KSTAT_STRLEN];    /* name of counter */
   uchar_t data_type;             /* data type */
   union {
            charc[16];            /* enough for 128-bit ints */
            struct {
               union {
                   char *ptr;    /* NULL-terminated string */
               } addr;
               uint32_t len;     /* length of string */
            } str;
            int32_t   i32;
            uint32_t  ui32;
            int64_t   i64;
            uint64_t  ui64;

  /* These structure members are obsolete */

            int32_t   l;
            uint32_t  ul;
            int64_t   ll;
            uint64_t  ull;
         } value;                /* value of counter */
} kstat_named_t;

I have mapped this in JNA as follows:

class KstatNamed extends Structure {
    public static class UNION extends Union {
        public byte[] charc = new byte[16]; // enough for 128-bit ints
        public Pointer str; // KstatNamedString
        public int i32;
        public int ui32;
        public long i64;
        public long ui64;
    }

    public byte[] name = new byte[KSTAT_STRLEN]; // name of counter
    public byte data_type; // data type
    public UNION value; // value of counter

    public KstatNamed() {
        super();
    }

    public KstatNamed(Pointer p) {
        super();
        this.useMemory(p);
        this.read();
    }

    @Override
    public void read() {
        super.read();
        switch (data_type) {
        case KSTAT_DATA_CHAR:
            value.setType(byte[].class);
            break;
        case KSTAT_DATA_STRING:
            value.setType(Pointer.class);
            break;
        case KSTAT_DATA_INT32:
        case KSTAT_DATA_UINT32:
            value.setType(int.class);
            break;
        case KSTAT_DATA_INT64:
        case KSTAT_DATA_UINT64:
            value.setType(long.class);
            break;
        default:
            break;
        }
        value.read();
    }

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList(new String[] { "name", "data_type", "value" });
    }
}

This code works correctly for int32 types (KSTAT_DATA_INT32). However, when the data type is KSTAT_DATA_STRING, which corresponds to the str structure inside the union, I am not having any success in properly retrieving the data.

I have mapped the nested structure like this:

class KstatNamedString extends Structure {
    public static class UNION extends Union {
        public Pointer ptr; // NULL-terminated string
    }

    public UNION addr;
    public int len; // length of string

    public KstatNamedString() {
        super();
    }

    public KstatNamedString(Pointer p) {
        super();
        this.useMemory(p);
        this.read();
    }

    @Override
    public void read() {
        super.read();
        addr.setType(Pointer.class);
        addr.read();
    }

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList(new String[] { "addr", "len" });
    }
}

Ultimately I'm trying to replicate the behavior of this C macro:

#define KSTAT_NAMED_STR_PTR(knptr) ((knptr)->value.str.addr.ptr)

I've tried multiple different methods of trying to get access to the above structure, but it never seems to read the correct data (the len value is in the millions and attempting to read the string ptr causes segfault). I've tried:

Pointer p = LibKstat.INSTANCE.kstat_data_lookup(ksp, name);
KstatNamed data = new KstatNamed(p);
KstatNamedString str = new KstatNamedString(data.value.str);
return str.addr.ptr.getString(0); // <--- Segfault on C side

I've also tried:

  • Specifying KstatNamedString as the type instead of the Pointer type
  • Using various combinations of ByReference in both the structures and the unions

I've googled everywhere, including trying what I thought was a promising result here, but nothing seems to work.

I'm sure I'm missing something simple.

4

1 回答 1

2

使用KstatNamedString而不是Pointer类型。

像这样更改基于指针的构造函数:

public KstatNamed(Pointer p) {
    super(p);
    this.read();
}

public KstatNamedString(Pointer p) {
    super(p);
    this.read();
}

并将结构字段的字段更改addrstr简单的Pointer(不需要它周围的联合位)。

public Pointer /*UNION*/ addr;

运行您的 JVM-Djna.dump_memory=true并将新初始化的字符串打印Structure为字符串。这将向您展示 JNA 如何解释结构的内存布局,以及如何初始化本机内存。这应该可以帮助您确定如何提取您正在寻找的字符串(假设它在那里)。

您还可以在设置联合类型之前将联合read()方法调整为最初只读取类型字段(使用Structure.readField("data_type"))。

于 2016-07-30T07:09:36.807 回答