mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-23 22:16:16 +01:00
Add dtrace and systemtap support for gobject
This adds static markers and systemtap tapsets for: * type creation * object lifetimes (creation, ref, unref, dispose, finalize) * signal creation and emission Signal emissions and finalization marker have a corresponding *_end (or *-end in dtrace) version that is when the corresponding operation is finished. https://bugzilla.gnome.org/show_bug.cgi?id=606044
This commit is contained in:
parent
bef9efd0a9
commit
8e41be13ef
@ -3592,6 +3592,7 @@ glib/tests/Makefile
|
||||
gmodule/Makefile
|
||||
gmodule/gmoduleconf.h
|
||||
gobject/Makefile
|
||||
gobject/gobject.stp
|
||||
gobject/glib-mkenums
|
||||
gobject/tests/Makefile
|
||||
gthread/Makefile
|
||||
|
@ -6,6 +6,8 @@ include $(top_srcdir)/Makefile.decl
|
||||
|
||||
SUBDIRS = . tests
|
||||
|
||||
CLEANFILES=
|
||||
|
||||
if HAVE_THREADS
|
||||
THREAD_FLAGS=-DG_THREADS_MANDATORY
|
||||
endif
|
||||
@ -120,11 +122,13 @@ gobject_private_h_sources = \
|
||||
|
||||
# GObject library C sources to build the library from
|
||||
gobject_c_sources = \
|
||||
gobject_probes.d \
|
||||
gatomicarray.c \
|
||||
gboxed.c \
|
||||
gclosure.c \
|
||||
genums.c \
|
||||
gobject.c \
|
||||
gobject_trace.h \
|
||||
gparam.c \
|
||||
gparamspecs.c \
|
||||
gsignal.c \
|
||||
@ -140,6 +144,24 @@ gobject_c_sources = \
|
||||
# these sources (also mentioned above) are generated.
|
||||
BUILT_SOURCES = gmarshal.h gmarshal.c gobjectalias.h gobjectaliasdef.c
|
||||
|
||||
if ENABLE_DTRACE
|
||||
gobject_probes.h: gobject_probes.d Makefile
|
||||
$(DTRACE) -C -h -s $< -o $@.tmp
|
||||
sed -e "s,define STAP_HAS_SEMAPHORES 1,undef STAP_HAS_SEMAPHORES," < $@.tmp > $@ && rm -f $@.tmp
|
||||
gobject_probes.o: gobject_probes.d Makefile
|
||||
$(DTRACE) -G -s $< -o $@
|
||||
BUILT_SOURCES += gobject_probes.h gobject_probes.o
|
||||
CLEANFILES += gobject_probes.h
|
||||
libgobject_2_0_la_LIBADD += gobject_probes.o
|
||||
endif
|
||||
|
||||
if ENABLE_SYSTEMTAP
|
||||
tapset_in_files = gobject.stp.in
|
||||
tapsetdir = $(DESTDIR)@ABS_TAPSET_DIR@
|
||||
tapset_DATA = $(tapset_in_files:.stp.in=.stp)
|
||||
EXTRA_DIST += $(tapset_in_files)
|
||||
endif
|
||||
|
||||
# non-header sources (headers should be specified in the above variables)
|
||||
# that don't serve as direct make target sources, i.e. they don't have
|
||||
# their own .lo rules and don't get publically installed
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "gsignal.h"
|
||||
#include "gparamspecs.h"
|
||||
#include "gvaluetypes.h"
|
||||
#include "gobject_trace.h"
|
||||
#include "gobjectalias.h"
|
||||
|
||||
/* This should be included after gobjectalias.h (or pltcheck.sh will fail) */
|
||||
@ -817,7 +818,9 @@ g_object_run_dispose (GObject *object)
|
||||
g_return_if_fail (object->ref_count > 0);
|
||||
|
||||
g_object_ref (object);
|
||||
TRACE (GOBJECT_OBJECT_DISPOSE(object,G_TYPE_FROM_INSTANCE(object), 0));
|
||||
G_OBJECT_GET_CLASS (object)->dispose (object);
|
||||
TRACE (GOBJECT_OBJECT_DISPOSE_END(object,G_TYPE_FROM_INSTANCE(object), 0));
|
||||
g_object_unref (object);
|
||||
}
|
||||
|
||||
@ -2406,7 +2409,9 @@ g_object_ref (gpointer _object)
|
||||
|
||||
if (old_val == 1 && OBJECT_HAS_TOGGLE_REF (object))
|
||||
toggle_refs_notify (object, FALSE);
|
||||
|
||||
|
||||
TRACE (GOBJECT_OBJECT_REF(object,G_TYPE_FROM_INSTANCE(object),old_val));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
@ -2422,7 +2427,6 @@ g_object_unref (gpointer _object)
|
||||
{
|
||||
GObject *object = _object;
|
||||
gint old_ref;
|
||||
gboolean is_zero;
|
||||
|
||||
g_return_if_fail (G_IS_OBJECT (object));
|
||||
g_return_if_fail (object->ref_count > 0);
|
||||
@ -2443,6 +2447,8 @@ g_object_unref (gpointer _object)
|
||||
if (!g_atomic_int_compare_and_exchange ((int *)&object->ref_count, old_ref, old_ref - 1))
|
||||
goto retry_atomic_decrement1;
|
||||
|
||||
TRACE (GOBJECT_OBJECT_UNREF(object,G_TYPE_FROM_INSTANCE(object),old_ref));
|
||||
|
||||
/* if we went from 2->1 we need to notify toggle refs if any */
|
||||
if (old_ref == 2 && has_toggle_ref) /* The last ref being held in this case is owned by the toggle_ref */
|
||||
toggle_refs_notify (object, TRUE);
|
||||
@ -2450,7 +2456,9 @@ g_object_unref (gpointer _object)
|
||||
else
|
||||
{
|
||||
/* we are about tp remove the last reference */
|
||||
TRACE (GOBJECT_OBJECT_DISPOSE(object,G_TYPE_FROM_INSTANCE(object), 1));
|
||||
G_OBJECT_GET_CLASS (object)->dispose (object);
|
||||
TRACE (GOBJECT_OBJECT_DISPOSE_END(object,G_TYPE_FROM_INSTANCE(object), 1));
|
||||
|
||||
/* may have been re-referenced meanwhile */
|
||||
retry_atomic_decrement2:
|
||||
@ -2463,25 +2471,33 @@ g_object_unref (gpointer _object)
|
||||
if (!g_atomic_int_compare_and_exchange ((int *)&object->ref_count, old_ref, old_ref - 1))
|
||||
goto retry_atomic_decrement2;
|
||||
|
||||
TRACE (GOBJECT_OBJECT_UNREF(object,G_TYPE_FROM_INSTANCE(object),old_ref));
|
||||
|
||||
/* if we went from 2->1 we need to notify toggle refs if any */
|
||||
if (old_ref == 2 && has_toggle_ref) /* The last ref being held in this case is owned by the toggle_ref */
|
||||
toggle_refs_notify (object, TRUE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* we are still in the process of taking away the last ref */
|
||||
g_datalist_id_set_data (&object->qdata, quark_closure_array, NULL);
|
||||
g_signal_handlers_destroy (object);
|
||||
g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL);
|
||||
|
||||
/* decrement the last reference */
|
||||
is_zero = g_atomic_int_dec_and_test ((int *)&object->ref_count);
|
||||
|
||||
old_ref = g_atomic_int_exchange_and_add ((int *)&object->ref_count, -1);
|
||||
|
||||
TRACE (GOBJECT_OBJECT_UNREF(object,G_TYPE_FROM_INSTANCE(object),old_ref));
|
||||
|
||||
/* may have been re-referenced meanwhile */
|
||||
if (G_LIKELY (is_zero))
|
||||
if (G_LIKELY (old_ref == 1))
|
||||
{
|
||||
TRACE (GOBJECT_OBJECT_FINALIZE(object,G_TYPE_FROM_INSTANCE(object)));
|
||||
G_OBJECT_GET_CLASS (object)->finalize (object);
|
||||
|
||||
TRACE (GOBJECT_OBJECT_FINALIZE_END(object,G_TYPE_FROM_INSTANCE(object)));
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
IF_DEBUG (OBJECTS)
|
||||
{
|
||||
|
199
gobject/gobject.stp.in
Normal file
199
gobject/gobject.stp.in
Normal file
@ -0,0 +1,199 @@
|
||||
global gtypes
|
||||
global gtypenames
|
||||
global gsignalnames
|
||||
|
||||
/* These are needed to keep track of gtype and signal names for the below
|
||||
* probes.
|
||||
*/
|
||||
probe process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("type__new")
|
||||
{
|
||||
gtypes[pid(),user_string($arg1)] = $arg3;
|
||||
gtypenames[pid(),$arg3] = user_string($arg1);
|
||||
}
|
||||
probe process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("signal__new")
|
||||
{
|
||||
gsignalnames[pid(),$arg1] = user_string($arg2);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe gobject.type_new - Called when any entity registered with the #GType system is created
|
||||
* @name: String name of type
|
||||
* @parent_gtype: The parent #GType of this type
|
||||
* @gtype: The #GType for this type
|
||||
*/
|
||||
probe gobject.type_new = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("type__new")
|
||||
{
|
||||
name = user_string($arg1);
|
||||
parent_gtype = $arg2;
|
||||
gtype = $arg3;
|
||||
probestr = sprintf("gobject.type_new(%s, %d) -> %d", name, parent_gtype, gtype);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe gobject.object_new - Called when a #GObject is created
|
||||
* @object: Raw pointer to object
|
||||
* @gtype: #GType for this object
|
||||
* @type: String name of object type
|
||||
*/
|
||||
probe gobject.object_new = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__new")
|
||||
{
|
||||
object = $arg1;
|
||||
gtype = $arg2;
|
||||
type = gtypenames[pid(),$arg2];
|
||||
probestr = sprintf("gobject.object_new(%s) -> %p", type, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe gobject.object_ref - Called when a new reference is added to a #GObject
|
||||
* @object: Raw pointer to object
|
||||
* @gtype: #GType for this object
|
||||
* @type: String name of object type
|
||||
* @old_refcount: Original value of the reference count
|
||||
* @refcount: New value of the reference count
|
||||
*/
|
||||
probe gobject.object_ref = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__ref")
|
||||
{
|
||||
object = $arg1;
|
||||
gtype = $arg2;
|
||||
type = gtypenames[pid(),gtype];
|
||||
old_refcount = $arg3;
|
||||
refcount = old_refcount+1;
|
||||
probestr = sprintf("gobject.object_ref(%p[%s]) -> %d", object, type, refcount);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe gobject.object_unref - Called when a reference is removed from a #GObject
|
||||
* @object: Raw pointer to object
|
||||
* @gtype: #GType for this object
|
||||
* @type: String name of object type
|
||||
* @old_refcount: Original value of the reference count
|
||||
*/
|
||||
probe gobject.object_unref = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__unref")
|
||||
{
|
||||
object = $arg1;
|
||||
gtype = $arg2;
|
||||
type = gtypenames[pid(),gtype];
|
||||
old_refcount = $arg3;
|
||||
refcount = old_refcount-1;
|
||||
probestr = sprintf("gobject.object_unref(%p [%s]) -> %d", object, type, refcount);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe gobject.object_dispose - Called when a g_object_dispose() run for a #GObject is initiated
|
||||
* @object: Raw pointer to object
|
||||
* @gtype: #GType for this object
|
||||
* @type: String name of object type
|
||||
* @last_unref: FIXME
|
||||
*/
|
||||
probe gobject.object_dispose = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__dispose")
|
||||
{
|
||||
object = $arg1;
|
||||
gtype = $arg2;
|
||||
type = gtypenames[pid(),$arg2];
|
||||
last_unref = $arg3;
|
||||
probestr = sprintf("gobject.object_dispose(%p[%s])", object, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe gobject.object_dispose_end - Called when a g_object_dispose() run for a #GObject is completed
|
||||
* @object: Raw pointer to object
|
||||
* @gtype: #GType for this object
|
||||
* @type: String name of object type
|
||||
* @last_unref: FIXME
|
||||
*/
|
||||
probe gobject.object_dispose_end = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__dispose__end")
|
||||
{
|
||||
object = $arg1;
|
||||
gtype = $arg2;
|
||||
type = gtypenames[pid(),$arg2];
|
||||
last_unref = $arg3;
|
||||
probestr = sprintf("gobject.object_dispose_end(%p[%s])", object, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe gobject.object_finalize - Called when finalization for a #GObject is started
|
||||
* @object: Raw pointer to object
|
||||
* @gtype: #GType for this object
|
||||
* @type: String name of object type
|
||||
*/
|
||||
probe gobject.object_finalize = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__finalize")
|
||||
{
|
||||
object = $arg1;
|
||||
gtype = $arg2;
|
||||
type = gtypenames[pid(),$arg2];
|
||||
probestr = sprintf("gobject.object_finalize(%p[%s])", object, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe gobject.object_finalize - Called when finalization for a #GObject is completed
|
||||
* @object: Raw pointer to object
|
||||
* @gtype: #GType for this object
|
||||
* @type: String name of object type
|
||||
*/
|
||||
probe gobject.object_finalize_end = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("object__finalize__end")
|
||||
{
|
||||
object = $arg1;
|
||||
gtype = $arg2;
|
||||
type = gtypenames[pid(),$arg2];
|
||||
probestr = sprintf("gobject.object_finalize_end(%p[%s])", object, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe gobject.signal_new - Called when a new signal is registered for a #GObject
|
||||
* @gsignal: Integer value for this signal
|
||||
* @name: String name for this signal
|
||||
* @gtype: #GType for the type which will gain the new signal
|
||||
* @type: String name of the type which will gain the new signal
|
||||
*/
|
||||
probe gobject.signal_new = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("signal__new")
|
||||
{
|
||||
gsignal = $arg1;
|
||||
name = user_string($arg2);
|
||||
gtype = $arg3;
|
||||
type = gtypenames[pid(),$arg3];
|
||||
probestr = sprintf("gobject.signal_new(%s, %s) -> %d", name, type, gsignal);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe gobject.signal_emit - Called when a signal emission for a #GObject is started
|
||||
* @gsignal: Integer value for this signal
|
||||
* @detail: String containing signal "detail"
|
||||
* @signal: String name of the signal
|
||||
* @object: Raw pointer for object emitting signal
|
||||
* @gtype: #GType for the type emitting the signal
|
||||
* @type: String name of the type emitting the signal
|
||||
*/
|
||||
probe gobject.signal_emit = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("signal__emit")
|
||||
{
|
||||
gsignal = $arg1;
|
||||
detail = $arg2;
|
||||
signal = gsignalnames[pid(),$arg1];
|
||||
if (detail != 0)
|
||||
signal = signal . "::" . gquarks[pid(), detail]
|
||||
object = $arg3;
|
||||
gtype = $arg4;
|
||||
type = gtypenames[pid(),$arg4];
|
||||
probestr = sprintf("gobject.signal_emit(%p[%s], %s)", object, type, signal);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe gobject.signal_emit_end - Called when a signal emission for a #GObject is completed
|
||||
* @gsignal: Integer value for this signal
|
||||
* @detail: String containing signal "detail"
|
||||
* @signal: String name of the signal
|
||||
* @object: Raw pointer for object emitting signal
|
||||
* @gtype: #GType for the type emitting the signal
|
||||
* @type: String name of the type emitting the signal
|
||||
*/
|
||||
probe gobject.signal_emit_end = process("@ABS_GLIB_RUNTIME_LIBDIR@/libgobject-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("signal__emit__end")
|
||||
{
|
||||
gsignal = $arg1;
|
||||
detail = $arg2;
|
||||
signal = gsignalnames[pid(),$arg1];
|
||||
if (detail != 0)
|
||||
signal = signal . "::" . gquarks[pid(), detail]
|
||||
object = $arg3;
|
||||
gtype = $arg4;
|
||||
type = gtypenames[pid(),$arg4];
|
||||
probestr = sprintf("gobject.signal_emit_end(%p[%s], %s)", object, type, signal);
|
||||
}
|
13
gobject/gobject_probes.d
Normal file
13
gobject/gobject_probes.d
Normal file
@ -0,0 +1,13 @@
|
||||
provider gobject {
|
||||
probe type__new(char *, unsigned long, unsigned long);
|
||||
probe object__new(void*, unsigned long);
|
||||
probe object__ref(void*, unsigned long, unsigned int);
|
||||
probe object__unref(void*, unsigned long, unsigned int);
|
||||
probe object__dispose(void*, unsigned long, unsigned int);
|
||||
probe object__dispose__end(void*, unsigned long, unsigned int);
|
||||
probe object__finalize(void*, unsigned long);
|
||||
probe object__finalize__end(void*, unsigned long);
|
||||
probe signal__new(unsigned int, char *, unsigned long)
|
||||
probe signal__emit(unsigned int, unsigned int, void *, unsigned long)
|
||||
probe signal__emit__end(unsigned int, unsigned int, void *, unsigned long)
|
||||
};
|
43
gobject/gobject_trace.h
Normal file
43
gobject/gobject_trace.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* GLIB - Library of useful routines for C programming
|
||||
*
|
||||
* Copyright (C) 2009,2010 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __GOBJECTTRACE_H__
|
||||
#define __GOBJECTTRACE_H__
|
||||
|
||||
#ifndef SIZEOF_CHAR
|
||||
#error "config.h must be included prior to gobject_trace.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DTRACE
|
||||
|
||||
/* include the generated probes header and put markers in code */
|
||||
#include "gobject_probes.h"
|
||||
#define TRACE(probe) probe
|
||||
|
||||
#else
|
||||
|
||||
/* Wrap the probe to allow it to be removed when no systemtap available */
|
||||
#define TRACE(probe)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __GOBJECTTRACE_H__ */
|
@ -36,6 +36,7 @@
|
||||
#include "gboxed.h"
|
||||
#include "gobject.h"
|
||||
#include "genums.h"
|
||||
#include "gobject_trace.h"
|
||||
#include "gobjectalias.h"
|
||||
|
||||
|
||||
@ -1608,6 +1609,8 @@ g_signal_newv (const gchar *signal_name,
|
||||
node->name = g_intern_string (name);
|
||||
key.quark = g_quark_from_string (name);
|
||||
g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);
|
||||
|
||||
TRACE(GOBJECT_SIGNAL_NEW(signal_id, name, itype));
|
||||
}
|
||||
node->destroyed = FALSE;
|
||||
node->test_class_offset = 0;
|
||||
@ -3124,7 +3127,9 @@ signal_emit_unlocked_R (SignalNode *node,
|
||||
G_BREAKPOINT ();
|
||||
}
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
|
||||
|
||||
TRACE(GOBJECT_SIGNAL_EMIT(node->signal_id, detail, instance, G_TYPE_FROM_INSTANCE (instance)));
|
||||
|
||||
SIGNAL_LOCK ();
|
||||
signal_id = node->signal_id;
|
||||
if (node->flags & G_SIGNAL_NO_RECURSE)
|
||||
@ -3382,7 +3387,9 @@ signal_emit_unlocked_R (SignalNode *node,
|
||||
SIGNAL_UNLOCK ();
|
||||
if (accumulator)
|
||||
g_value_unset (&accu);
|
||||
|
||||
|
||||
TRACE(GOBJECT_SIGNAL_EMIT_END(node->signal_id, detail, instance, G_TYPE_FROM_INSTANCE (instance)));
|
||||
|
||||
return return_value_altered;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "gbsearcharray.h"
|
||||
#include "gobjectalias.h"
|
||||
#include "gatomicarray.h"
|
||||
#include "gobject_trace.h"
|
||||
|
||||
|
||||
/**
|
||||
@ -481,7 +482,9 @@ type_node_any_new_W (TypeNode *pnode,
|
||||
pnode->children = g_renew (GType, pnode->children, pnode->n_children);
|
||||
pnode->children[i] = type;
|
||||
}
|
||||
|
||||
|
||||
TRACE(GOBJECT_TYPE_NEW(name, node->supers[1], type));
|
||||
|
||||
node->plugin = plugin;
|
||||
node->n_children = 0;
|
||||
node->children = NULL;
|
||||
@ -1883,7 +1886,9 @@ g_type_create_instance (GType type)
|
||||
instance->g_class = class;
|
||||
if (node->data->instance.instance_init)
|
||||
node->data->instance.instance_init (instance, class);
|
||||
|
||||
|
||||
TRACE(GOBJECT_OBJECT_NEW(instance, type));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user