[BUG] Bus Error in kstat extension

D

Daniel Berger

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
 
T

ts

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
 
D

Daniel Berger

ts said:
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
 
T

ts

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
 
T

ts

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
 
D

Daniel Berger

ts said:
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
 
C

Charles Mills

If you were going to take a very conservative approach...

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?
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;
}
(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
 
T

ts

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
 
D

Daniel Berger

Charles Mills said:
If you were going to take a very conservative approach...

Shouldn't all these hashes be volatile?

Yes, I think so.
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.
volatile rbHash?

Yes.

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
 
C

Charles Mills

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,962
Messages
2,570,134
Members
46,690
Latest member
MacGyver

Latest Threads

Top