[BUG] Bus Error in kstat extension

Discussion in 'Ruby' started by Daniel Berger, Sep 28, 2004.

  1. Hi all,

    Ruby 1.8.2 (2004-09-22)
    Solaris 9
    gcc 3.3.3

    I'm trying to build an extension for kstat on Solaris. Sample test
    scripts work fine, but test unit blows up with a bus error. That
    usually means I've got a problem somewhere in my C code with regards
    to memory allocation. Only, I don't see where.

    Here's the .c, .h, extconf.rb, test.rb, and tc_kstat.rb files,
    followed by the backtrace:

    /* rkstat.c */
    #include <kstat.h>
    #include <sys/sysinfo.h>
    #include <sys/inttypes.h>
    #include "ruby.h"
    #include "rkstat.h"

    VALUE cKstatError;

    static VALUE ks_allocate(VALUE klass){
    KstatStruct* ptr = malloc(sizeof(KstatStruct));
    return Data_Wrap_Struct(klass,0,ks_free,ptr);
    }

    VALUE ks_init(VALUE self){
    KstatStruct* ptr;

    Data_Get_Struct(self,KstatStruct,ptr);

    ptr->kc = kstat_open();
    if(NULL == ptr->kc){
    rb_raise(cKstatError,"kstat_open() failure");
    }

    ptr->ksp = kstat_lookup(
    ptr->kc,
    NULL,
    -1,
    NULL
    );

    if(NULL == ptr->ksp){
    rb_raise(cKstatError,"kstat_lookup() failure");
    }

    return self;
    }

    VALUE ks_record(VALUE self){
    VALUE rbMHash, rbIHash, rbNHash, rbSHash;
    KstatStruct* ptr;
    kstat_io_t kio;

    Data_Get_Struct(self,KstatStruct,ptr);

    rbMHash = rb_hash_new(); // Module name is key, holds rbIHashes
    rbIHash = rb_hash_new(); // Instance name is key, holds rbNHashes
    rbNHash = rb_hash_new(); // Name is key, holds rbSHashes

    kstat_chain_update(ptr->kc); // Sync the chain with the kernel

    for(
    ptr->ksp = ptr->kc->kc_chain;
    ptr->ksp != NULL;
    ptr->ksp = ptr->ksp->ks_next
    )
    {
    switch(ptr->ksp->ks_type){
    case KSTAT_TYPE_NAMED:
    kstat_read(ptr->kc,ptr->ksp,NULL);
    rbSHash = map_named_data_type(ptr->ksp);
    break;
    case KSTAT_TYPE_IO:
    kstat_read(ptr->kc,ptr->ksp,&kio);
    rbSHash = map_io_data_type(kio);
    break;
    }
    rb_hash_aset(rbNHash,rb_str_new2(ptr->ksp->ks_name),rbSHash);
    rb_hash_aset(rbIHash,INT2FIX(ptr->ksp->ks_instance), rbNHash);
    rb_hash_aset(rbMHash,rb_str_new2(ptr->ksp->ks_module),rbIHash);
    }

    return rbMHash;
    }

    void Init_kstat(){
    VALUE mSolaris, cKstat;

    // Module and Class declarations
    mSolaris = rb_define_module("Solaris");
    cKstat = rb_define_class_under(mSolaris,"Kstat",rb_cObject);
    cKstatError = rb_define_class_under(mSolaris,"KstatError",rb_eStandardError);
    rb_define_alloc_func(cKstat,ks_allocate);

    // Instance Methods
    rb_define_method(cKstat,"initialize",ks_init,0);
    rb_define_method(cKstat,"record",ks_record,0);

    // Constants
    rb_define_const(cKstat,"VERSION",rb_str_new2(SOLARIS_KSTAT_VERSION));
    }

    /* rkstat.h */
    #define SOLARIS_KSTAT_VERSION "0.1.0"

    static VALUE map_named_data_type(kstat_t* ksp);

    // Structure wrapped as our Kstat class
    struct kstruct{
    kstat_ctl_t* kc;
    kstat_t* ksp;
    };

    typedef struct kstruct KstatStruct;

    static void ks_free(KstatStruct* p){
    kstat_close(p->kc);
    free(p);
    }

    static VALUE map_io_data_type(kstat_io_t k){
    VALUE rbHash = rb_hash_new();

    rb_hash_aset(rbHash,rb_str_new2("nread"),ULL2NUM(k.nread));
    rb_hash_aset(rbHash,rb_str_new2("nwritten"),ULL2NUM(k.nwritten));
    rb_hash_aset(rbHash,rb_str_new2("reads"),UINT2NUM(k.reads));
    rb_hash_aset(rbHash,rb_str_new2("writes"),UINT2NUM(k.writes));
    rb_hash_aset(rbHash,rb_str_new2("wtime"),ULL2NUM(k.wtime));
    rb_hash_aset(rbHash,rb_str_new2("wlentime"),ULL2NUM(k.wlentime));
    rb_hash_aset(rbHash,rb_str_new2("wlastupdate"),ULL2NUM(k.wlastupdate));
    rb_hash_aset(rbHash,rb_str_new2("rtime"),ULL2NUM(k.rtime));
    rb_hash_aset(rbHash,rb_str_new2("rlentime"),ULL2NUM(k.rlentime));
    rb_hash_aset(rbHash,rb_str_new2("rlastupdate"),ULL2NUM(k.rlastupdate));
    rb_hash_aset(rbHash,rb_str_new2("wcnt"),UINT2NUM(k.wcnt));
    rb_hash_aset(rbHash,rb_str_new2("rcnt"),UINT2NUM(k.rcnt));

    return rbHash;
    }

    static VALUE map_named_data_type(kstat_t* ksp){
    VALUE rbHash;
    kstat_named_t* knp;
    knp = (kstat_named_t *)ksp->ks_data;
    int i;

    rbHash = rb_hash_new();

    for(i = 0; i < ksp->ks_ndata; i++, knp++)
    {
    switch (knp->data_type)
    {
    case KSTAT_DATA_CHAR:
    rb_hash_aset(rbHash,rb_str_new2(knp->name),rb_str_new2(knp->value.c));
    break;
    case KSTAT_DATA_INT32:
    rb_hash_aset(rbHash,rb_str_new2(knp->name),INT2NUM(knp->value.i32));
    break;
    case KSTAT_DATA_UINT32:
    rb_hash_aset(rbHash,rb_str_new2(knp->name),UINT2NUM(knp->value.ui32));
    break;
    case KSTAT_DATA_INT64:
    rb_hash_aset(rbHash,rb_str_new2(knp->name),LL2NUM(knp->value.i64));
    break;
    case KSTAT_DATA_UINT64:
    rb_hash_aset(rbHash,rb_str_new2(knp->name),ULL2NUM(knp->value.ui64));
    break;
    default:
    rb_hash_aset(rbHash,rb_str_new2(knp->name),rb_str_new2("Unknown"));
    break;
    }
    }
    return rbHash;
    }

    # extconf.rb
    require "mkmf"
    require "ftools"

    # This package requires Solaris 2.8 or later
    unless have_header("kstat.h")
    STDERR.puts "The kstat.h header file was not found. Exiting."
    exit
    end

    have_library("kstat")
    create_makefile("solaris/kstat")


    # test.rb - works fine
    require "solaris/kstat"
    include Solaris

    k1 = Kstat.new
    k2 = Kstat.new

    p k1.record["cpu_info"][0]["cpu_info0"]
    p k1.record["nfs"][1]["nfs1"]

    p k2.record["cpu_info"][0]["cpu_info0"]
    p k2.record["nfs"][1]["nfs1"]

    # tc_kstat.rb - causes bus error.

    require "solaris/kstat"
    require "test/unit"
    include Solaris

    class TC_Kstat < Test::Unit::TestCase
    def setup
    @k = Kstat.new
    end

    def test_version
    assert_equal("0.1.0",Kstat::VERSION)
    end

    def test_record_basic
    assert_respond_to(@k, :record)
    end

    # If you remove this test or the test_record_io it works,
    # but if you try to run both it causes a bus error.
    def test_record_named
    assert_nothing_raised{ @k.record["cpu_info"][0]["cpu_info0"] }
    assert_kind_of(Hash, @k.record["cpu_info"][0]["cpu_info0"])
    end

    def test_record_io
    assert_nothing_raised{ @k.record["nfs"][1]["nfs1"] }
    assert_kind_of(Hash, @k.record["nfs"][1]["nfs1"])
    end

    def teardown
    @k = nil
    end
    end

    Here's the backtrace:
    djberge@sp5wd-b1-/home/djberge/workspace/solaris-kstat-995>gdb
    /opt/bin/ruby core
    GNU gdb 6.1
    Copyright 2004 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and
    you are
    welcome to change it and/or distribute copies of it under certain
    conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB. Type "show warranty" for
    details.
    This GDB was configured as "sparc-sun-solaris2.8"...(no debugging
    symbols found)...
    Core was generated by `ruby test/tc_kstat.rb'.
    Program terminated with signal 6, Aborted.
    Reading symbols from /usr/lib/libdl.so.1...(no debugging symbols
    found)...done.
    Loaded symbols for /usr/lib/libdl.so.1
    Reading symbols from /usr/lib/libm.so.1...(no debugging symbols
    found)...done.
    Loaded symbols for /usr/lib/libm.so.1
    Reading symbols from /usr/lib/libc.so.1...(no debugging symbols
    found)...done.
    Loaded symbols for /usr/lib/libc.so.1
    Reading symbols from
    /usr/platform/SUNW,Sun-Blade-100/lib/libc_psr.so.1...(no debugging
    symbols found)...done.
    Loaded symbols for /usr/platform/SUNW,Sun-Blade-100/lib/libc_psr.so.1
    Reading symbols from ./solaris/kstat.so...(no debugging symbols
    found)...done.
    Loaded symbols for ./solaris/kstat.so
    Reading symbols from /usr/lib/libkstat.so.1...(no debugging symbols
    found)...done.
    Loaded symbols for /usr/lib/libkstat.so.1
    #0 0xff31d3d4 in _libc_kill () from /usr/lib/libc.so.1
    (gdb) backtrace
    #0 0xff31d3d4 in _libc_kill () from /usr/lib/libc.so.1
    #1 0xff2b5698 in abort () from /usr/lib/libc.so.1
    #2 0x000ed618 in rb_bug ()
    #3 0x000b5e50 in sigbus ()
    #4 <signal handler called>
    #5 0x000bad34 in st_foreach ()
    #6 0x00046490 in mark_tbl ()
    #7 0x00046a68 in gc_mark_children ()
    #8 0x000466ec in gc_mark ()
    #9 0x000464f0 in mark_keyvalue ()
    #10 0x000bada8 in st_foreach ()
    #11 0x00046544 in mark_hash ()
    #12 0x00046b6c in gc_mark_children ()
    #13 0x000466ec in gc_mark ()
    #14 0x000463c4 in mark_locations_array ()
    #15 0x00046410 in rb_gc_mark_locations ()
    #16 0x000482dc in rb_gc ()
    #17 0x00045dbc in rb_newobj ()
    #18 0x000bb068 in str_alloc ()
    #19 0x000bb148 in str_new ()
    #20 0x000bb1f8 in rb_str_new ()
    #21 0x000bb25c in rb_str_new2 ()
    #22 0xff261294 in map_named_data_type () from ./solaris/kstat.so
    #23 0xff261594 in ks_record () from ./solaris/kstat.so
    #24 0x0003ca10 in call_cfunc ()
    #25 0x0002a38c in rb_call0 ()
    #26 0x0002b27c in rb_call ()
    #27 0x00022950 in rb_eval ()
    #28 0x00022708 in rb_eval ()
    #29 0x00022708 in rb_eval ()
    #30 0x00022708 in rb_eval ()
    #31 0x00027a1c in rb_yield_0 ()
    #32 0x00021978 in rb_eval ()
    #33 0x00021a44 in rb_eval ()
    #34 0x00027a1c in rb_yield_0 ()
    #35 0x00021978 in rb_eval ()
    #36 0x00022278 in rb_eval ()
    #37 0x00021d2c in rb_eval ()
    #38 0x0002ab0c in rb_call0 ()
    #39 0x0002b27c in rb_call ()
    ---Type <return> to continue, or q <return> to quit---
    #40 0x00022c1c in rb_eval ()
    #41 0x00021380 in rb_eval ()
    #42 0x0002ab0c in rb_call0 ()
    #43 0x0002b27c in rb_call ()
    #44 0x00022c1c in rb_eval ()
    #45 0x00021380 in rb_eval ()
    #46 0x0002ab0c in rb_call0 ()
    #47 0x0002b27c in rb_call ()
    #48 0x0002b418 in rb_f_send ()
    #49 0x0003c9f0 in call_cfunc ()
    #50 0x0002a38c in rb_call0 ()
    #51 0x0002b27c in rb_call ()
    #52 0x00022c1c in rb_eval ()
    #53 0x00021a44 in rb_eval ()
    #54 0x00021d2c in rb_eval ()
    #55 0x0002ab0c in rb_call0 ()
    #56 0x0002b27c in rb_call ()
    #57 0x00022950 in rb_eval ()
    #58 0x000329ac in block_pass ()
    #59 0x000210c8 in rb_eval ()
    #60 0x00027a1c in rb_yield_0 ()
    #61 0x00027fd0 in rb_yield ()
    #62 0x000d853c in rb_ary_each ()
    #63 0x0003ca10 in call_cfunc ()
    #64 0x0002a38c in rb_call0 ()
    #65 0x0002b27c in rb_call ()
    #66 0x00022950 in rb_eval ()
    #67 0x00021380 in rb_eval ()
    #68 0x0002ab0c in rb_call0 ()
    #69 0x0002b27c in rb_call ()
    #70 0x00022950 in rb_eval ()
    #71 0x000329ac in block_pass ()
    #72 0x000210c8 in rb_eval ()
    #73 0x00027a1c in rb_yield_0 ()
    #74 0x00027fd0 in rb_yield ()
    #75 0x000d853c in rb_ary_each ()
    #76 0x0003ca10 in call_cfunc ()
    #77 0x0002a38c in rb_call0 ()
    #78 0x0002b27c in rb_call ()
    #79 0x00022950 in rb_eval ()
    ---Type <return> to continue, or q <return> to quit---
    #80 0x00021380 in rb_eval ()
    #81 0x0002ab0c in rb_call0 ()
    #82 0x0002b27c in rb_call ()
    #83 0x00022950 in rb_eval ()
    #84 0x00021380 in rb_eval ()
    #85 0x0002ab0c in rb_call0 ()
    #86 0x0002b27c in rb_call ()
    #87 0x00022950 in rb_eval ()
    #88 0x00022278 in rb_eval ()
    #89 0x0002ab0c in rb_call0 ()
    #90 0x0002b27c in rb_call ()
    #91 0x00022c60 in rb_eval ()
    #92 0x00022278 in rb_eval ()
    #93 0x0002ab0c in rb_call0 ()
    #94 0x0002b27c in rb_call ()
    #95 0x00022950 in rb_eval ()
    #96 0x00022278 in rb_eval ()
    #97 0x0002ab0c in rb_call0 ()
    #98 0x0002b27c in rb_call ()
    #99 0x00022950 in rb_eval ()
    #100 0x00022708 in rb_eval ()
    #101 0x0002ab0c in rb_call0 ()
    #102 0x0002b27c in rb_call ()
    #103 0x00022950 in rb_eval ()
    #104 0x0002ab0c in rb_call0 ()
    #105 0x0002b27c in rb_call ()
    #106 0x00022950 in rb_eval ()
    #107 0x00022ac0 in rb_eval ()
    #108 0x00027a1c in rb_yield_0 ()
    #109 0x00031e98 in proc_invoke ()
    #110 0x0002f9a0 in call_end_proc ()
    #111 0x0002fddc in rb_exec_end_proc ()
    #112 0x0001c6c8 in ruby_finalize_0 ()
    #113 0x0001c768 in ruby_cleanup ()
    #114 0x0001cab4 in ruby_stop ()
    #115 0x0001cb4c in ruby_run ()
    #116 0x00019870 in main ()

    Any ideas? Thanks.

    Dan
     
    Daniel Berger, Sep 28, 2004
    #1
    1. Advertising

  2. Daniel Berger

    ts Guest

    >>>>> "D" == Daniel Berger <> writes:

    D> static VALUE ks_allocate(VALUE klass){
    D> KstatStruct* ptr = malloc(sizeof(KstatStruct));
    D> return Data_Wrap_Struct(klass,0,ks_free,ptr);
    D> }

    I don't understand this when you have a nice Data_Make_Struct()

    D> VALUE ks_record(VALUE self){
    D> VALUE rbMHash, rbIHash, rbNHash, rbSHash;

    VALUE rbMHash, rbIHash, rbNHash;
    volatile VALUE rbSHash;


    Guy Decoux
     
    ts, Sep 29, 2004
    #2
    1. Advertising

  3. ts <> wrote in message news:<>...
    > >>>>> "D" == Daniel Berger <> writes:

    >
    > D> static VALUE ks_allocate(VALUE klass){
    > D> KstatStruct* ptr = malloc(sizeof(KstatStruct));
    > D> return Data_Wrap_Struct(klass,0,ks_free,ptr);
    > D> }
    >
    > I don't understand this when you have a nice Data_Make_Struct()


    This didn't make any difference. Although, you're right, I should
    just use Data_Make_Struct() instead. I think I'm still suffering undo
    paranoia from past "Data_Make_Struct is unsafe" threads.

    > D> VALUE ks_record(VALUE self){
    > D> VALUE rbMHash, rbIHash, rbNHash, rbSHash;
    >
    > VALUE rbMHash, rbIHash, rbNHash;
    > volatile VALUE rbSHash;


    Unfortunately, this didn't make any difference, either. It still
    dumps core.

    Any other ideas?

    Dan
     
    Daniel Berger, Sep 29, 2004
    #3
  4. Daniel Berger

    ts Guest

    >>>>> "D" == Daniel Berger <> writes:

    D> Unfortunately, this didn't make any difference, either. It still
    D> dumps core.

    D> Any other ideas?

    no, but I've an old ruby


    nasun% make ; cp kstat.so solaris ; ruby b.rb
    gcc -fPIC -g -O2 -I. -I/usr/local/lib/ruby/1.8/sparc-solaris2.8 -I/usr/local/lib/ruby/1.8/sparc-solaris2.8 -I. -DHAVE_KSTAT_H -c rkstat.c
    gcc -Wl,-G -L"/usr/local/lib" -o kstat.so rkstat.o -lkstat -ldl -lcrypt -lm -lc
    Loaded suite b
    Started
    ..b.rb:21: [BUG] rb_gc_mark(): unknown data type 0x28(0xffbec018) non object
    ruby 1.8.1 (2003-12-25) [sparc-solaris2.8]

    Abort (core dumped)
    nasun%

    nasun% diff -u rkstat.c~ rkstat.c
    --- rkstat.c~ Wed Sep 29 16:54:15 2004
    +++ rkstat.c Wed Sep 29 16:55:00 2004
    @@ -97,7 +97,7 @@

    VALUE ks_record(VALUE self){
    VALUE rbMHash, rbIHash, rbNHash;
    - VALUE rbSHash;
    + volatile VALUE rbSHash;
    KstatStruct* ptr;
    kstat_io_t kio;

    nasun% make ; cp kstat.so solaris ; ruby b.rb
    gcc -fPIC -g -O2 -I. -I/usr/local/lib/ruby/1.8/sparc-solaris2.8 -I/usr/local/lib/ruby/1.8/sparc-solaris2.8 -I. -DHAVE_KSTAT_H -c rkstat.c
    gcc -Wl,-G -L"/usr/local/lib" -o kstat.so rkstat.o -lkstat -ldl -lcrypt -lm -lc
    Loaded suite b
    Started
    ....
    Finished in 0.152439 seconds.

    4 tests, 6 assertions, 0 failures, 0 errors
    nasun%


    Guy Decoux
     
    ts, Sep 29, 2004
    #4
  5. Daniel Berger

    ts Guest

    >>>>> "t" == ts <> writes:

    t> no, but I've an old ruby

    nasun% ruby -v b.rb
    ruby 1.8.2 (2004-09-29) [sparc-solaris2.8]
    Loaded suite b
    Started
    ....
    Finished in 0.156774 seconds.

    4 tests, 6 assertions, 0 failures, 0 errors
    nasun%



    Guy Decoux
     
    ts, Sep 29, 2004
    #5
  6. ts <> wrote in message news:<>...
    > >>>>> "D" == Daniel Berger <> writes:

    >
    > D> static VALUE ks_allocate(VALUE klass){
    > D> KstatStruct* ptr = malloc(sizeof(KstatStruct));
    > D> return Data_Wrap_Struct(klass,0,ks_free,ptr);
    > D> }
    >
    > I don't understand this when you have a nice Data_Make_Struct()
    >
    > D> VALUE ks_record(VALUE self){
    > D> VALUE rbMHash, rbIHash, rbNHash, rbSHash;
    >
    > VALUE rbMHash, rbIHash, rbNHash;
    > volatile VALUE rbSHash;
    >
    >
    > Guy Decoux


    Argh! I forgot to mention that I built Ruby with the
    --disable-largefile option. Once I rebuilt Ruby without that option,
    it worked, although with warnings:

    In file included from /opt/lib/ruby/1.8/sparc-solaris2.9/ruby.h:21,
    from rkstat.c:4:
    /opt/lib/ruby/1.8/sparc-solaris2.9/config.h:16:1: warning:
    "_FILE_OFFSET_BITS" redefined
    In file included from
    /opt/csw/gcc3/lib/gcc-lib/sparc-sun-solaris2.8/3.3.3/include/sys/types.h:28,
    from /usr/include/kstat.h:10,
    from rkstat.c:1:
    /usr/include/sys/feature_tests.h:96:1: warning: this is the location
    of the previous definition

    However, this puts me in a dilemma, since removing that option means I
    can't build any extensions that use procfs.h, unless I want to build
    with the -m64 option (which I don't). See ruby-talk 67160 and 77026
    for more on that issue.

    Any suggestions from here? How were you able to build without that
    warning? And do you know how to resolve the procfs.h issue?

    Regards,

    Dan
     
    Daniel Berger, Sep 29, 2004
    #6
  7. If you were going to take a very conservative approach...

    On Sep 28, 2004, at 11:04 AM, Daniel Berger wrote:

    > Hi all,
    >
    > Ruby 1.8.2 (2004-09-22)
    > Solaris 9
    > gcc 3.3.3
    >
    > I'm trying to build an extension for kstat on Solaris. Sample test
    > scripts work fine, but test unit blows up with a bus error. That
    > usually means I've got a problem somewhere in my C code with regards
    > to memory allocation. Only, I don't see where.
    >
    > Here's the .c, .h, extconf.rb, test.rb, and tc_kstat.rb files,
    > followed by the backtrace:
    >
    > /* rkstat.c */
    > #include <kstat.h>
    > #include <sys/sysinfo.h>
    > #include <sys/inttypes.h>
    > #include "ruby.h"
    > #include "rkstat.h"
    >
    > VALUE cKstatError;
    >
    > static VALUE ks_allocate(VALUE klass){
    > KstatStruct* ptr = malloc(sizeof(KstatStruct));
    > return Data_Wrap_Struct(klass,0,ks_free,ptr);
    > }
    >
    > VALUE ks_init(VALUE self){
    > KstatStruct* ptr;
    >
    > Data_Get_Struct(self,KstatStruct,ptr);
    >
    > ptr->kc = kstat_open();
    > if(NULL == ptr->kc){
    > rb_raise(cKstatError,"kstat_open() failure");
    > }
    >
    > ptr->ksp = kstat_lookup(
    > ptr->kc,
    > NULL,
    > -1,
    > NULL
    > );
    >
    > if(NULL == ptr->ksp){
    > rb_raise(cKstatError,"kstat_lookup() failure");
    > }
    >
    > return self;
    > }
    >
    > VALUE ks_record(VALUE self){
    > VALUE rbMHash, rbIHash, rbNHash, rbSHash;


    Shouldn't all these hashes be volatile?
    <snip>
    >
    > static VALUE map_io_data_type(kstat_io_t k){
    > VALUE rbHash = rb_hash_new();
    >


    volatile rbHash?

    > rb_hash_aset(rbHash,rb_str_new2("nread"),ULL2NUM(k.nread));
    > rb_hash_aset(rbHash,rb_str_new2("nwritten"),ULL2NUM(k.nwritten));
    > rb_hash_aset(rbHash,rb_str_new2("reads"),UINT2NUM(k.reads));
    > rb_hash_aset(rbHash,rb_str_new2("writes"),UINT2NUM(k.writes));
    > rb_hash_aset(rbHash,rb_str_new2("wtime"),ULL2NUM(k.wtime));
    > rb_hash_aset(rbHash,rb_str_new2("wlentime"),ULL2NUM(k.wlentime));
    >
    > rb_hash_aset(rbHash,rb_str_new2("wlastupdate"),ULL2NUM(k.wlastupdate));
    > rb_hash_aset(rbHash,rb_str_new2("rtime"),ULL2NUM(k.rtime));
    > rb_hash_aset(rbHash,rb_str_new2("rlentime"),ULL2NUM(k.rlentime));
    >
    > rb_hash_aset(rbHash,rb_str_new2("rlastupdate"),ULL2NUM(k.rlastupdate));
    > rb_hash_aset(rbHash,rb_str_new2("wcnt"),UINT2NUM(k.wcnt));
    > rb_hash_aset(rbHash,rb_str_new2("rcnt"),UINT2NUM(k.rcnt));
    >


    Also, ULL2NUM() (and all the 2NUM() macros) can call rb_gc() if the
    argument is out of Fixnum range (a Bignum will be created - possibly
    calling rb_gc() in the process). This could cause the key argument to
    be collected by the gc (I think).

    > return rbHash;
    > }
    >
    > static VALUE map_named_data_type(kstat_t* ksp){
    > VALUE rbHash;


    volatile rbHash?

    > kstat_named_t* knp;
    > knp = (kstat_named_t *)ksp->ks_data;
    > int i;
    >
    > rbHash = rb_hash_new();
    >
    > for(i = 0; i < ksp->ks_ndata; i++, knp++)
    > {
    > switch (knp->data_type)
    > {
    > case KSTAT_DATA_CHAR:
    >
    > rb_hash_aset(rbHash,rb_str_new2(knp->name),rb_str_new2(knp->value.c));
    > break;
    > case KSTAT_DATA_INT32:
    >
    > rb_hash_aset(rbHash,rb_str_new2(knp->name),INT2NUM(knp->value.i32));
    > break;
    > case KSTAT_DATA_UINT32:
    >
    > rb_hash_aset(rbHash,rb_str_new2(knp->name),UINT2NUM(knp->value.ui32));
    > break;
    > case KSTAT_DATA_INT64:
    >
    > rb_hash_aset(rbHash,rb_str_new2(knp->name),LL2NUM(knp->value.i64));
    > break;
    > case KSTAT_DATA_UINT64:
    >
    > rb_hash_aset(rbHash,rb_str_new2(knp->name),ULL2NUM(knp->value.ui64));
    > break;
    > default:
    >
    > rb_hash_aset(rbHash,rb_str_new2(knp->name),rb_str_new2("Unknown"));
    > break;


    rb_str_new2() can call gc() (more below with backtrace). In the first
    case and the last case of this switch statement rb_str_new2() is called
    twice, BEFORE rb_hash_aset() is called. If the second call to
    rb_str_new2() in these cases calls the rb_gc() you have problems.
    You could use a function like:

    /* untested code */
    void
    hash_add_pair(VALUE hash, const char *key, const char *value)
    {
    volatile VALUE key_obj = rb_str_new2(key);
    rb_hash_aset(hash, key_obj, rb_str_new2(value));
    }


    > }
    > }
    > return rbHash;
    > }
    >

    <snip>
    > (gdb) backtrace
    > #0 0xff31d3d4 in _libc_kill () from /usr/lib/libc.so.1
    > #1 0xff2b5698 in abort () from /usr/lib/libc.so.1
    > #2 0x000ed618 in rb_bug ()
    > #3 0x000b5e50 in sigbus ()
    > #4 <signal handler called>
    > #5 0x000bad34 in st_foreach ()
    > #6 0x00046490 in mark_tbl ()
    > #7 0x00046a68 in gc_mark_children ()
    > #8 0x000466ec in gc_mark ()
    > #9 0x000464f0 in mark_keyvalue ()
    > #10 0x000bada8 in st_foreach ()
    > #11 0x00046544 in mark_hash ()
    > #12 0x00046b6c in gc_mark_children ()
    > #13 0x000466ec in gc_mark ()
    > #14 0x000463c4 in mark_locations_array ()
    > #15 0x00046410 in rb_gc_mark_locations ()
    > #16 0x000482dc in rb_gc ()
    > #17 0x00045dbc in rb_newobj ()


    gc is being called by rb_str_new2() in map_named_data_type()
    seems like the most likely place for an error because of the back to
    back calls of rb_str_new2()

    > #18 0x000bb068 in str_alloc ()
    > #19 0x000bb148 in str_new ()
    > #20 0x000bb1f8 in rb_str_new ()
    > #21 0x000bb25c in rb_str_new2 ()



    > #22 0xff261294 in map_named_data_type () from ./solaris/kstat.so
    > #23 0xff261594 in ks_record () from ./solaris/kstat.so
    >

    <snip>

    Disclaimer: these are suggestions... I am by no means an expert.
    -Charlie
     
    Charles Mills, Sep 29, 2004
    #7
  8. Daniel Berger

    ts Guest

    >>>>> "D" == Daniel Berger <> writes:

    D> How were you able to build without that warning?

    I change the order of include in rkstat.c (ruby.h in the first place)


    Guy Decoux
     
    ts, Sep 29, 2004
    #8
  9. Charles Mills <> wrote in message news:<>...
    > If you were going to take a very conservative approach...

    <snip>

    > > VALUE ks_record(VALUE self){
    > > VALUE rbMHash, rbIHash, rbNHash, rbSHash;

    >
    > Shouldn't all these hashes be volatile?


    Yes, I think so.

    > > static VALUE map_io_data_type(kstat_io_t k){
    > > VALUE rbHash = rb_hash_new();


    > volatile rbHash?


    Yes

    > Also, ULL2NUM() (and all the 2NUM() macros) can call rb_gc() if the
    > argument is out of Fixnum range (a Bignum will be created - possibly
    > calling rb_gc() in the process). This could cause the key argument to
    > be collected by the gc (I think).


    I'd like to find out for certain. I'll have to dig. However, I
    didn't tinker with these, and things seem to be working fine.

    > > static VALUE map_named_data_type(kstat_t* ksp){
    > > VALUE rbHash;

    >
    > volatile rbHash?


    Yes.

    <snip>
    > rb_str_new2() can call gc() (more below with backtrace). In the first
    > case and the last case of this switch statement rb_str_new2() is called
    > twice, BEFORE rb_hash_aset() is called. If the second call to
    > rb_str_new2() in these cases calls the rb_gc() you have problems.
    > You could use a function like:
    >
    > /* untested code */
    > void
    > hash_add_pair(VALUE hash, const char *key, const char *value)
    > {
    > volatile VALUE key_obj = rb_str_new2(key);
    > rb_hash_aset(hash, key_obj, rb_str_new2(value));
    > }


    This wasn't actually necessary in testing, but it seems like a good
    idea in general. It's something I plan to do in the future. :)

    > Disclaimer: these are suggestions... I am by no means an expert.
    > -Charlie


    Well, between you and Guy, I think it's solved - THANKS! Now, I just
    need the "Dummy's Guide to 'Volatile' in Ruby Extensions". :)

    Regards,

    Dan
     
    Daniel Berger, Sep 30, 2004
    #9
  10. On Sep 29, 2004, at 4:20 PM, Daniel Berger wrote:
    >
    >> Also, ULL2NUM() (and all the 2NUM() macros) can call rb_gc() if the
    >> argument is out of Fixnum range (a Bignum will be created - possibly
    >> calling rb_gc() in the process). This could cause the key argument to
    >> be collected by the gc (I think).

    >
    > I'd like to find out for certain. I'll have to dig. However, I
    > didn't tinker with these, and things seem to be working fine.

    It is probably a rare case where the arguments of ULL2NUM() and friends
    are out of Fixnum range, but if they are bignew_1() in bignum.c will be
    called which calls ALLOC_N() which can call rb_gc()... so a number of
    things will have to happen before *2NUM() starts causing problems.
    If you browse the Ruby sources much I have some stuff I use to run
    Doxygen on the Ruby sources posted my webpage:
    http://cmills.freeshell.org/
    ...
    Another little optimization I though of right after I sent that email
    is freezing String keys before you call rb_hash_aset...
    So to set your hash elements safely you could do:
    void
    hash_add_pair(VALUE hash, const char *key, volatile VALUE val)
    {
    rb_hash_aset(hash, rb_str_freeze(rb_str_new2(key)), val);
    }

    Saves some memory allocation and takes away the ULL2NUM() worries.

    >
    > Well, between you and Guy, I think it's solved - THANKS! Now, I just
    > need the "Dummy's Guide to 'Volatile' in Ruby Extensions". :)


    Glad to hear you got everything working. Funny you mentioned the
    "Dummy's Guide to 'Volatile' in Ruby Extensions" because after I sent
    that email to you I started thinking about what are good rules for when
    to use volatile and when not to. Here is my understanding... somebody
    please chime in if this is wrong:

    - Objects passed as parameters don't need to be placed in volatile
    storage.
    Unless your hacking the interpreter functions don't have to make sure
    their parameters are safe from the GC (not the functions
    responcibility).

    - If you create an object in your function you need to make sure it is
    in volatile storage, unless you are not going to call any functions
    which may call the GC after the object is created.

    - All functions which allocate (or reallocate) memory, rb_file_open(),
    and probably a few others can call the GC.

    - If your placing object in a structure (like Array or Hash) which is
    protected from the GC then those objects are protected / get marked.

    Here are a couple contrived and untested examples:

    VALUE
    my_name_is(const char *name)
    {
    return rb_str_new2(name);
    }
    ##
    Obviously you don't need to worry about wether or not the Ruby String
    created in the above function is going to be collected by the GC.
    However,
    ##
    VALUE
    my_fullname_is(const char *first, const char *last)
    {
    VALUE fullname = rb_str_new2(first);
    rb_str_append(fullname, rb_str_new2(" "));
    return rb_str_append(fullname, rb_str_new2(last));
    }
    ##
    the variable fullname above should be volatile. Is this true if we use
    rb_str_cat2() instead of append?
    ##
    void
    push_name_pair(VALUE ary, const char *first, const char *last)
    {
    VALUE pair = rb_ary_new2(2);
    rb_ary_push(ary, pair); /* now pair is safe */

    /* pair will mark the strings created from first and last */
    rb_ary_push(pair, rb_str_new2(first));
    rb_ary_push(pair, rb_str_new2(last));
    }
    ##
    As far as I can tell everything in the above function is safe from the
    gc.

    -Charlie
     
    Charles Mills, Sep 30, 2004
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Anand Patil
    Replies:
    0
    Views:
    398
    Anand Patil
    Jan 30, 2007
  2. Sam Roberts
    Replies:
    4
    Views:
    233
    Eric Hodel
    Dec 6, 2004
  3. Daniel Berger

    [ANN] solaris-kstat 0.2.0

    Daniel Berger, Jan 18, 2005, in forum: Ruby
    Replies:
    0
    Views:
    85
    Daniel Berger
    Jan 18, 2005
  4. Replies:
    0
    Views:
    85
  5. Diego Virasoro
    Replies:
    4
    Views:
    109
    Nobuyoshi Nakada
    Oct 24, 2006
Loading...

Share This Page