[Kde-bindings] Another segfault yet, but more complex

Richard Dale Richard_Dale at tipitina.demon.co.uk
Tue Oct 5 20:32:27 UTC 2004


On Tuesday 05 October 2004 19:13, Han Holl wrote:
> Hello Richard,
>
> I'vr got another segfault for you, but I haven't succeeded in creating a
> _very_ simple script to demonstrate.
>
> I've investigated somewhat myself, and added debugging information around
> the call to checkarg, like this:
>
> (0...args.length).each do |i|
>     tmp = checkarg( getVALUEtype(args[i]), getTypeNameOfArg(id, i) )
>     puts "ISCORE: #{i} #{tmp} #{args[i]} #{getVALUEtype(args[i])}
> #{getTypeNameOfArg(id, i)}" if debug_level >= DebugLevel::High
>    current_match += tmp
> end
>
> (Sorry for the lousy formatting). Anyway, what happens is that at some
> point I get this output:
> ISCORE: 0 -99 #<Qt::Widget:0x43230f68> QObject QWidget*
>
> which means that even though the object args[i] is Qt::Widget:0x43230f68,
> getVALUEtype(args[i]) returns QObject (instead of QWidget).
> What happens then that checharg return -99, and a segfault.
>
> I've prepared an archive which hopefully runs standalone. Because it turns
> out to be only 7kb I've attached it directly to this post.
>
> I hope you can find out what's happening.
Well confusing.. 

I think I know what the problem is. In handlers.cpp there is a function called 
resolve_classname(), which uses various Qt RTTI mechanims to get a 
better/more accurate class name to instantiate as a ruby instance. 

For instance, if there is a method returning say a 'QObject*' in the api and 
it returns an instance that was constructed in the C++ world (or it might be 
a ruby instance which went out of scope and got garbage collected, but the 
C++ instance wasn't deleted). Then it is possible to use QObject::className() 
to find out what the real class name of the QObject instance is. If you add 
this debugging to the code just before the show page:

puts "parent class: #{self.parent} #{self.parent.className}"

It prints this:
parent class: #<Qt::Widget:0x30466e38> Qt::Widget

So both ruby and Qt are agreed that it's a Qt::Widget, only Smoke thinks it's 
a QObject. If it was a 'QWidget' rather than a 'Qt::Widget' that would mean 
it had been allocated in the C++ world. So here the ruby instance was garbage 
collected, but not the underlying C++ instance. Subsequently the C++ instance 
was rewrapped as a Qt::Widget.

Associated with each instance is a struct, which holds a 'void *' pointer to 
the underlying C++ instance, along with a numerice identifer, the 'int 
classId' which is an index into an array of class names:

struct smokeruby_object {
    bool allocated;
    Smoke *smoke;
    int classId;
    void *ptr;
};
 
It's this classId int which is wrong in this case. Here's the relevant code in 
handlers.cpp:

  smokeruby_object  * o = (smokeruby_object *) 
malloc(sizeof(smokeruby_object));
  o->smoke = m->smoke();
  o->classId = m->type().classId();
  o->ptr = p;
  o->allocated = false;

  const char * classname = resolve_classname(m, o->ptr);

  ...
  
  obj = set_obj_info(classname, o);

It leaves the o->classId value as it was (representing a Smoke QObject in this 
case), even though resolve_classname() has improved on it. set_obj_info() 
then uses that classname to construct the ruby instance, like this:

VALUE
set_obj_info(const char * className, smokeruby_object * o)
{
    VALUE klass = rb_funcall(qt_internal_module,
        rb_intern("find_class"),
        1,
        rb_str_new2(className) );
    VALUE obj = Data_Wrap_Struct(klass, smokeruby_mark, smokeruby_free, (void 
*) o);
    return obj;
}

This improved method arg type matching really has thrown up a whole pile of 
interesting bugs. I fixed a couple of things with the rbuic tool today too as 
a result..

I would make widget an instance variable @widget in the meantime, so ruby 
doesn't garbage collect it, along with anything else long lived. You can 
debug garbage collection like this:

Qt::Internal::setDebug(Qt::QtDebugChannel::QTDB_GC)

-- Richard



More information about the Kde-bindings mailing list