Upgrade to Pro — share decks privately, control downloads, hide ads and more …

code generation from C in gopsutil

shirou
June 21, 2015

code generation from C in gopsutil

Generate go code from C struct use godefs. This technique is used in the gopsutil, goport of psutil.

shirou

June 21, 2015
Tweet

More Decks by shirou

Other Decks in Technology

Transcript

  1. go code generation

    from C
    r_rudi / एࢁ ࢙࿠

    View Slide

  2. Who are you?
    • @r_rudi / WAKAYAMA Shirou
    • CTO of Tsukinowa inc.
    • ansible / MQTT / Sphinx
    !
    • No.6 of Go GitHub developers in Japan

    View Slide

  3. psutil
    • A cross-platform process and system utilities
    module for Python
    • Linux/Windows/OSX/FreeBSD/Solaris
    • 32bit/64bit
    • CPU, Mem, Disk, Network, Process and so on

    View Slide

  4. gopsutil
    • go port of the psutil
    • github.com/shirou/gopsutil
    • Linux/Windows/OSX/FreeBSD
    • 32bit/64bit
    • “Pure Golang"

    View Slide

  5. How to get system information
    • Linux has proc filesystem
    • read under “/proc”
    • /proc/stat
    • /proc/mem
    • /proc/10/stat
    Thanks it’s Text

    View Slide

  6. FreeBSD/OSX
    • Some of values can get from sysctl as Text
    !
    • Some is Not Text
    • kern.devstat.all retunes “C struct devinfo_dev”
    • can not use cgo to stay “Pure golang”
    • (Linux /var/run/utmp is also not text)
    % sysctl vm.stats.vm.v_page_count
    vm.stats.vm.v_page_count: 1012332

    View Slide

  7. Use syscall.Syscall
    • call syscall of low-level operating system
    primitives directory
    Notice:

    http://golang.org/pkg shows only Linux. 

    you must read src directory to read other platform

    View Slide

  8. How to use syscall
    // freebsd MIB

    mib := []int32{CTLKern, KernDevstat, KernDevstatAll}


    // invoke syscall

    _, _, err = syscall.Syscall6(
    syscall.SYS___SYSCTL,
    uintptr(unsafe.Pointer(&mib[0])),
    uintptr(miblen),
    uintptr(unsafe.Pointer(&buf[0])),
    uintptr(unsafe.Pointer(&length)),
    0,
    0)

    // parse bytes to Devstat

    var ds Devstat
    br := bytes.NewReader(buf)
    Read(br, binary.LittleEndian, &ds)

    View Slide

  9. struct Devstat from C to Go
    type Devstat struct {
    Sequence0 uint32
    Allocated int32
    Start_count uint32
    End_count uint32
    Busy_from Bintime
    Dev_links _Ctype_struct___0
    Device_number uint32
    Device_name [16]int8
    Unit_number int32
    Bytes [4]uint64
    Operations [4]uint64
    Duration [4]Bintime
    Busy_time Bintime
    Creation_time Bintime
    Block_size uint32
    Pad_cgo_0 [4]byte
    Tag_types [3]uint64
    Flags uint32
    Device_type uint32
    Priority uint32
    Pad_cgo_1 [4]byte
    Id *byte
    Sequence1 uint32
    Pad_cgo_2 [4]byte
    }
    struct devstat {
    /* Internal house-keeping fields */
    u_int sequence0; /* Update sequence# */
    int allocated; /* Allocated entry */
    u_int start_count; /* started ops */
    u_int end_count; /* completed ops */
    struct bintime busy_from; /* */
    STAILQ_ENTRY(devstat) dev_links;
    u_int32_t device_number; /*
    * Devstat device
    * number.
    */
    char device_name[DEVSTAT_NAME_LEN];
    int unit_number;
    u_int64_t bytes[DEVSTAT_N_TRANS_FLAGS];
    u_int64_t operations[DEVSTAT_N_TRANS_FLAGS];
    struct bintime duration[DEVSTAT_N_TRANS_FLAGS];
    struct bintime busy_time;
    struct bintime creation_time; /*
    * Time the device was
    * created.
    */
    u_int32_t block_size; /* Block size, bytes */
    u_int64_t tag_types[3]; /*
    * The number of
    * simple, ordered,
    * and head of queue
    * tags sent.
    write by your self? Oh no no no!

    View Slide

  10. Use “cgo -godefs"
    // +build ignore
    package disk
    !
    /*
    #include
    #include
    #include
    */
    import "C"
    !
    type Devstat C.struct_devstat
    % go tool cgo -godefs types_freebsd.go > disk_freebsd_amd64.go
    types_freebsd.go
    Once you created definition, can use on other hosts.

    View Slide

  11. godefs
    • godef refer other struct if defined
    type Devstat C.struct_devstat

    type Bintime C.struct_bintime
    struct devstat {

    struct bintime busy_time;
    };

    struct bintime {
    time_t sec;
    uint64_t frac;
    };
    +
    type Devstat struct {
    Busy_time Bintime

    }

    type Bintime struct {
    Sec int64
    Frac uint64
    }

    View Slide

  12. limitation
    • union
    • C's union types are represented as a Go
    byte array with the same length
    • output perfect may not perfect
    • some hand editing should be required…

    View Slide

  13. Ok, how about Windows?
    • No need to use C struct
    !
    !
    !
    !
    !
    • easy, huh?
    Modkernel32 = syscall.NewLazyDLL("kernel32.dll")

    procGetDiskFreeSpaceExW = Modkernel32.NewProc("GetDiskFreeSpaceExW")


    lpFreeBytesAvailable := int64(0)
    lpTotalNumberOfBytes := int64(0)
    lpTotalNumberOfFreeBytes := int64(0)

    ret, _, err := procGetDiskFreeSpaceExW.Call(

    uintptr(unsafe.Pointer(

    syscall.StringToUTF16Ptr(path))),
    uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),
    uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),
    uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)))

    View Slide

  14. Conclusion
    • gopsutil can get system information
    • Linux/Windows/OSX/FreeBSD
    • use syscall.Syscall to do low-level
    • use “cgo -godefs” to generate Go from C

    View Slide