Uname: Linux web3.us.cloudlogin.co 5.10.226-xeon-hst #2 SMP Fri Sep 13 12:28:44 UTC 2024 x86_64
Software: Apache
PHP version: 8.1.31 [ PHP INFO ] PHP os: Linux
Server Ip: 162.210.96.117
Your Ip: 18.222.87.144
User: edustar (269686) | Group: tty (888)
Safe Mode: OFF
Disable Function:
NONE

name : railsbench.patch
diff --git a/a/gc.c b/b/gc.c
index bcdc9af..b380842 100644
--- a/a/gc.c
+++ b/b/gc.c
@@ -23,8 +23,16 @@
 #include <setjmp.h>
 #include <sys/types.h>
 
+#ifdef _WIN32
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
+#elif defined(_WIN32)
+#include <time.h> 
 #endif
 
 #ifdef HAVE_SYS_RESOURCE_H
@@ -211,11 +219,11 @@ getrusage_time(void)
 	if (objspace->profile.run) {\
 	    size_t count = objspace->profile.count;\
 	    objspace->profile.record[count].heap_use_slots = heaps_used;\
-	    objspace->profile.record[count].heap_live_objects = live;\
+	    objspace->profile.record[count].heap_live_objects = live_objects;\
 	    objspace->profile.record[count].heap_free_objects = freed;\
 	    objspace->profile.record[count].heap_total_objects = heaps_used * HEAP_OBJ_LIMIT;\
 	    objspace->profile.record[count].have_finalize = final_list ? Qtrue : Qfalse;\
-	    objspace->profile.record[count].heap_use_size = live * sizeof(RVALUE);\
+	    objspace->profile.record[count].heap_use_size = live_objects * sizeof(RVALUE);\
 	    objspace->profile.record[count].heap_total_size = heaps_used * (HEAP_OBJ_LIMIT * sizeof(RVALUE));\
 	}\
     } while(0)
@@ -232,7 +240,7 @@ getrusage_time(void)
 	if (objspace->profile.run) {\
 	    size_t count = objspace->profile.count;\
 	    objspace->profile.record[count].heap_total_objects = heaps_used * HEAP_OBJ_LIMIT;\
-	    objspace->profile.record[count].heap_use_size = live * sizeof(RVALUE);\
+	    objspace->profile.record[count].heap_use_size = live_objects * sizeof(RVALUE);\
 	    objspace->profile.record[count].heap_total_size = heaps_used * HEAP_SIZE;\
 	}\
     } while(0)
@@ -372,7 +380,7 @@ rb_objspace_alloc(void)
 {
     rb_objspace_t *objspace = malloc(sizeof(rb_objspace_t));
     memset(objspace, 0, sizeof(*objspace));
-    malloc_limit = GC_MALLOC_LIMIT;
+    malloc_limit = initial_malloc_limit;
     ruby_gc_stress = ruby_initial_gc_stress;
 
     return objspace;
@@ -405,6 +413,170 @@ int ruby_disable_gc_stress = 0;
 static void run_final(rb_objspace_t *objspace, VALUE obj);
 static int garbage_collect(rb_objspace_t *objspace);
 
+static unsigned long live_objects = 0;
+unsigned long rb_os_live_objects()
+{ return live_objects; }
+
+#if defined(HAVE_LONG_LONG)
+static unsigned long long allocated_objects = 0;
+unsigned long long rb_os_allocated_objects()
+{ return allocated_objects; }
+#else
+static unsigned long allocated_objects = 0;
+unsigned long rb_os_allocated_objects()
+{ return allocated_objects; }
+#endif
+
+static int heap_min_slots = HEAP_MIN_SLOTS;
+static int heap_free_min = 4096;
+static double heap_slots_growth_factor = 1.8;
+
+static long initial_malloc_limit = GC_MALLOC_LIMIT;
+static int verbose_gc_stats = Qfalse;
+static FILE* gc_data_file = NULL;
+
+static void set_gc_parameters()
+{
+    char *gc_stats_ptr, *min_slots_ptr, *free_min_ptr, *heap_slots_incr_ptr,
+      *heap_incr_ptr, *malloc_limit_ptr, *gc_heap_file_ptr, *heap_slots_growth_factor_ptr;
+
+    gc_data_file = stderr;
+
+    gc_stats_ptr = getenv("RUBY_GC_STATS");
+    if (gc_stats_ptr != NULL) {
+	int gc_stats_i = atoi(gc_stats_ptr);
+	if (gc_stats_i > 0) {
+	    verbose_gc_stats = Qtrue;
+	}
+    }
+
+    gc_heap_file_ptr = getenv("RUBY_GC_DATA_FILE");
+    if (gc_heap_file_ptr != NULL) {
+	FILE* data_file = fopen(gc_heap_file_ptr, "w");
+	if (data_file != NULL) {
+	    gc_data_file = data_file;
+	}
+	else {
+	    fprintf(stderr,
+		    "can't open gc log file %s for writing, using default\n", gc_heap_file_ptr);
+	}
+    }
+
+    min_slots_ptr = getenv("RUBY_HEAP_MIN_SLOTS");
+    if (min_slots_ptr != NULL) {
+	int min_slots_i = atoi(min_slots_ptr);
+        if (verbose_gc_stats) {
+	    fprintf(gc_data_file, "RUBY_HEAP_MIN_SLOTS=%s\n", min_slots_ptr);
+        }
+	if (min_slots_i > 0) {
+	    heap_min_slots = min_slots_i;
+	}
+    }
+
+    free_min_ptr = getenv("RUBY_HEAP_FREE_MIN");
+    if (free_min_ptr != NULL) {
+	int free_min_i = atoi(free_min_ptr);
+        if (verbose_gc_stats) {
+	    fprintf(gc_data_file, "RUBY_HEAP_FREE_MIN=%s\n", free_min_ptr);
+	}
+	if (free_min_i > 0) {
+	    heap_free_min = free_min_i;
+	}
+    }
+
+    heap_incr_ptr = getenv("RUBY_HEAP_INCREMENT");
+    if (heap_incr_ptr != NULL) {
+	int heap_incr_i = atoi(heap_incr_ptr);
+        if (verbose_gc_stats) {
+	    fprintf(gc_data_file, "RUBY_HEAP_INCREMENT=%s\n", heap_incr_ptr);
+	}
+	if (heap_incr_i > 0) {
+	    (&rb_objspace)->malloc_params.increase = heap_incr_i;
+	}
+    }
+
+    heap_slots_incr_ptr = getenv("RUBY_HEAP_SLOTS_INCREMENT");
+    if (heap_slots_incr_ptr != NULL) {
+	int heap_slots_incr_i = atoi(heap_slots_incr_ptr);
+        if (verbose_gc_stats) {
+	    fprintf(gc_data_file, "RUBY_HEAP_SLOTS_INCREMENT=%s\n", heap_slots_incr_ptr);
+	}
+	if (heap_slots_incr_i > 0) {
+	    (&rb_objspace)->malloc_params.increase = heap_slots_incr_i;
+	}
+    }
+
+    heap_slots_growth_factor_ptr = getenv("RUBY_HEAP_SLOTS_GROWTH_FACTOR");
+    if (heap_slots_growth_factor_ptr != NULL) {
+	double heap_slots_growth_factor_d = atoi(heap_slots_growth_factor_ptr);
+        if (verbose_gc_stats) {
+	    fprintf(gc_data_file, "RUBY_HEAP_SLOTS_GROWTH_FACTOR=%s\n", heap_slots_growth_factor_ptr);
+	}
+	if (heap_slots_growth_factor_d > 0) {
+	    heap_slots_growth_factor = heap_slots_growth_factor_d;
+	}
+    }
+
+    malloc_limit_ptr = getenv("RUBY_GC_MALLOC_LIMIT");
+    if (malloc_limit_ptr != NULL) {
+	int malloc_limit_i = atol(malloc_limit_ptr);
+        if (verbose_gc_stats) {
+	    fprintf(gc_data_file, "RUBY_GC_MALLOC_LIMIT=%s\n", malloc_limit_ptr);
+	}
+	if (malloc_limit_i > 0) {
+	    initial_malloc_limit = malloc_limit_i;
+	}
+    }
+}
+
+/*
+ *  call-seq:
+ *     GC.dump    => nil
+ *
+ *  dumps information about the current GC data structures to the GC log file
+ *
+ *     GC.dump    #=> nil
+ *
+ */
+
+VALUE
+rb_gc_dump()
+{
+    int i;
+    rb_objspace_t *objspace = &rb_objspace;
+
+    for (i = 0; i < heaps_used; i++) {
+        int heap_size = heaps[i].limit;
+        fprintf(gc_data_file, "HEAP[%2d]: size=%7d\n", i, heap_size);
+    }
+
+    return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     GC.log String  => String
+ *
+ *  Logs string to the GC data file and returns it.
+ *
+ *     GC.log "manual GC call"    #=> "manual GC call"
+ *
+ */
+
+VALUE
+rb_gc_log(VALUE self, VALUE original_str)
+{
+    if (original_str == Qnil) {
+        fprintf(gc_data_file, "\n");
+    }
+    else {
+        VALUE str = StringValue(original_str);
+        char *p = RSTRING_PTR(str);
+        fprintf(gc_data_file, "%s\n", p);
+    }
+    return original_str;
+}
+
 void
 rb_global_variable(VALUE *var)
 {
@@ -454,6 +626,10 @@ rb_memerror(void)
     rb_exc_raise(nomem_error);
 }
 
+long gc_allocated_size = 0;
+long gc_num_allocations = 0;
+static int gc_statistics = 0;
+
 /*
  *  call-seq:
  *    GC.stress                 => true or false
@@ -699,6 +875,10 @@ vm_xfree(rb_objspace_t *objspace, void *ptr)
 void *
 ruby_xmalloc(size_t size)
 {
+    if (gc_statistics) {
+      gc_allocated_size += size;
+      gc_num_allocations += 1;
+    }
     return vm_xmalloc(&rb_objspace, size);
 }
 
@@ -744,6 +924,13 @@ ruby_xfree(void *x)
       vm_xfree(&rb_objspace, x);
 }
 
+#if HAVE_LONG_LONG
+#define GC_TIME_TYPE LONG_LONG
+#else
+#define GC_TIME_TYPE long
+#endif
+static GC_TIME_TYPE gc_time = 0;
+static int gc_collections = 0;
 
 /*
  *  call-seq:
@@ -775,7 +962,7 @@ rb_gc_enable(void)
  *  Disables garbage collection, returning <code>true</code> if garbage
  *  collection was already disabled.
  *
- *     GC.disable   #=> false
+ *     GC.disable   #=> false or true
  *     GC.disable   #=> true
  *
  */
@@ -790,6 +977,139 @@ rb_gc_disable(void)
     return old;
 }
 
+/*
+ *  call-seq:
+ *     GC.enable_stats    => true or false
+ *
+ *  Enables garbage collection statistics, returning <code>true</code> if garbage
+ *  collection statistics was already enabled.
+ *
+ *     GC.enable_stats   #=> false or true
+ *     GC.enable_stats   #=> true
+ *
+ */
+
+VALUE
+rb_gc_enable_stats()
+{
+    int old = gc_statistics;
+    gc_statistics = Qtrue;
+    return old;
+}
+
+/*
+ *  call-seq:
+ *     GC.disable_stats    => true or false
+ *
+ *  Disables garbage collection statistics, returning <code>true</code> if garbage
+ *  collection statistics was already disabled.
+ *
+ *     GC.disable_stats   #=> false or true
+ *     GC.disable_stats   #=> true
+ *
+ */
+
+VALUE
+rb_gc_disable_stats()
+{
+    int old = gc_statistics;
+    gc_statistics = Qfalse;
+    gc_allocated_size = 0;
+    gc_num_allocations = 0;
+    return old;
+}
+
+/*
+ *  call-seq:
+ *     GC.clear_stats    => nil
+ *
+ *  Clears garbage collection statistics, returning nil. This resets the number
+ *  of collections (GC.collections) and the time used (GC.time) to 0.
+ *
+ *     GC.clear_stats    #=> nil
+ *
+ */
+
+VALUE
+rb_gc_clear_stats()
+{
+    gc_collections = 0;
+    gc_time = 0;
+    gc_allocated_size = 0;
+    gc_num_allocations = 0;
+    return Qnil; 
+}
+
+/*
+ *  call-seq:
+ *     GC.allocated_size    => Integer
+ *
+ *  Returns the size of memory (in bytes) allocated since GC statistics collection
+ *  was enabled.
+ *
+ *     GC.allocated_size    #=> 35
+ *
+ */
+VALUE
+rb_gc_allocated_size()
+{
+    return INT2NUM(gc_allocated_size);
+}
+
+/*
+ *  call-seq:
+ *     GC.num_allocations    => Integer
+ *
+ *  Returns the number of memory allocations since GC statistics collection
+ *  was enabled.
+ *
+ *     GC.num_allocations    #=> 150
+ *
+ */
+VALUE
+rb_gc_num_allocations()
+{
+    return INT2NUM(gc_num_allocations);
+}
+
+/*
+ *  call-seq:
+ *     GC.collections    => Integer
+ *
+ *  Returns the number of garbage collections performed while GC statistics collection
+ *  was enabled.
+ *
+ *     GC.collections    #=> 35
+ *
+ */
+
+VALUE
+rb_gc_collections()
+{
+    return INT2NUM(gc_collections);
+}
+
+/*
+ *  call-seq:
+ *     GC.time    => Integer
+ *
+ *  Returns the time spent during garbage collection while GC statistics collection
+ *  was enabled (in micro seconds).
+ *
+ *     GC.time    #=> 20000
+ *
+ */
+
+VALUE
+rb_gc_time()
+{
+#if HAVE_LONG_LONG
+    return LL2NUM(gc_time);
+#else
+    return LONG2NUM(gc_time);
+#endif
+}
+
 VALUE rb_mGC;
 
 void
@@ -921,7 +1241,7 @@ init_heap(rb_objspace_t *objspace)
 {
     size_t add, i;
 
-    add = HEAP_MIN_SLOTS / HEAP_OBJ_LIMIT;
+    add = heap_min_slots / HEAP_OBJ_LIMIT;
 
     if ((heaps_used + add) > heaps_length) {
     	allocate_heaps(objspace, heaps_used + add);
@@ -934,12 +1254,12 @@ init_heap(rb_objspace_t *objspace)
     objspace->profile.invoke_time = getrusage_time();
 }
 
-
 static void
 set_heaps_increment(rb_objspace_t *objspace)
 {
-    size_t next_heaps_length = heaps_used * 1.8;
+    size_t next_heaps_length = heaps_used * heap_slots_growth_factor;
     heaps_inc = next_heaps_length - heaps_used;
+    if (next_heaps_length <= 0) next_heaps_length = heap_min_slots;
 
     if (next_heaps_length > heaps_length) {
 	allocate_heaps(objspace, next_heaps_length);
@@ -980,6 +1300,8 @@ rb_newobj_from_heap(rb_objspace_t *objspace)
     RANY(obj)->line = rb_sourceline();
 #endif
 
+    live_objects++;
+    allocated_objects++;
     return obj;
 }
 
@@ -1671,6 +1993,39 @@ finalize_list(rb_objspace_t *objspace, RVALUE *p)
     }
 }
 
+static char* obj_type(int tp)
+{
+    switch (tp) {
+	case T_NIL    : return "NIL";   
+	case T_OBJECT : return "OBJECT";
+	case T_CLASS  : return "CLASS";
+	case T_ICLASS : return "ICLASS";
+	case T_MODULE : return "MODULE";
+	case T_FLOAT  : return "FLOAT";
+	case T_STRING : return "STRING";
+	case T_REGEXP : return "REGEXP";
+	case T_ARRAY  : return "ARRAY";
+	case T_FIXNUM : return "FIXNUM";
+	case T_HASH   : return "HASH";
+	case T_STRUCT : return "STRUCT";
+	case T_BIGNUM : return "BIGNUM";
+	case T_FILE   : return "FILE";
+	    
+	case T_TRUE   : return "TRUE";
+	case T_FALSE  : return "FALSE";
+	case T_DATA   : return "DATA";
+	case T_MATCH  : return "MATCH";
+	case T_SYMBOL : return "SYMBOL";
+	    
+	//case T_BLKTAG : return "BLKTAG";
+	case T_UNDEF  : return "UNDEF";
+	case T_RATIONAL : return "RATIONAL";
+	case T_COMPLEX  : return "COMPLEX";
+	case T_NODE   : return "NODE";
+	default: return "____";
+    }
+}
+
 static void
 free_unused_heaps(rb_objspace_t *objspace)
 {
@@ -1711,14 +2066,24 @@ gc_sweep(rb_objspace_t *objspace)
     RVALUE *p, *pend, *final_list;
     size_t freed = 0;
     size_t i;
-    size_t live = 0, free_min = 0, do_heap_free = 0;
+    size_t free_min = 0, do_heap_free = 0;
+    live_objects = 0;
+
+    unsigned long really_freed = 0;
+    int free_counts[256];
+    int live_counts[256];
+    int do_gc_stats = gc_statistics & verbose_gc_stats;
 
     do_heap_free = (heaps_used * HEAP_OBJ_LIMIT) * 0.65;
     free_min = (heaps_used * HEAP_OBJ_LIMIT)  * 0.2;
 
-    if (free_min < FREE_MIN) {
+    if (free_min < heap_free_min) {
 	do_heap_free = heaps_used * HEAP_OBJ_LIMIT;
-        free_min = FREE_MIN;
+        free_min = heap_free_min;
+    }
+    
+    if (do_gc_stats) {
+	for (i = 0 ; i< 256; i++) { free_counts[i] = live_counts[i] = 0; }
     }
 
     freelist = 0;
@@ -1739,6 +2104,9 @@ gc_sweep(rb_objspace_t *objspace)
 		    if (!deferred) {
 			p->as.free.flags = T_ZOMBIE;
 			RDATA(p)->dfree = 0;
+	    if (do_gc_stats) {
+  			really_freed++;
+	    }
 		    }
 		    p->as.free.flags |= FL_MARK;
 		    p->as.free.next = final_list;
@@ -1746,6 +2114,12 @@ gc_sweep(rb_objspace_t *objspace)
 		    final_num++;
 		}
 		else {
+	      if (do_gc_stats) {
+			    int obt = p->as.basic.flags & T_MASK;
+		      if (obt) {
+	      free_counts[obt]++;
+			    }
+	      }
 		    add_freelist(objspace, p);
 		    free_num++;
 		}
@@ -1756,7 +2130,10 @@ gc_sweep(rb_objspace_t *objspace)
 	    }
 	    else {
 		RBASIC(p)->flags &= ~FL_MARK;
-		live++;
+		live_objects++;
+		if (do_gc_stats) {
+ 		    live_counts[RANY((VALUE)p)->as.basic.flags & T_MASK]++;
+ 		}
 	    }
 	    p++;
 	}
@@ -1777,8 +2154,8 @@ gc_sweep(rb_objspace_t *objspace)
     }
     GC_PROF_SET_MALLOC_INFO;
     if (malloc_increase > malloc_limit) {
-	malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed);
-	if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
+	malloc_limit += (malloc_increase - malloc_limit) * (double)live_objects / (live_objects + freed);
+	if (malloc_limit < initial_malloc_limit) malloc_limit = initial_malloc_limit;
     }
     malloc_increase = 0;
     if (freed < free_min) {
@@ -1786,6 +2163,20 @@ gc_sweep(rb_objspace_t *objspace)
 	heaps_increment(objspace);
     }
     during_gc = 0;
+    
+    if (do_gc_stats) {
+	fprintf(gc_data_file, "objects processed: %.7d\n", live_objects+freed);
+	fprintf(gc_data_file, "live objects	: %.7d\n", live_objects);
+	fprintf(gc_data_file, "freelist objects : %.7d\n", freed - really_freed);
+	fprintf(gc_data_file, "freed objects	: %.7d\n", really_freed);
+	for(i=0; i<256; i++) {
+	  if (free_counts[i]>0) {
+	fprintf(gc_data_file,
+		"kept %.7d / freed %.7d objects of type %s\n",
+		live_counts[i], free_counts[i], obj_type(i));
+    }
+	}
+    }
 
     /* clear finalization list */
     if (final_list) {
@@ -1994,6 +2385,7 @@ garbage_collect(rb_objspace_t *objspace)
 {
     struct gc_list *list;
     rb_thread_t *th = GET_THREAD();
+    struct timeval gctv1, gctv2;
     INIT_GC_PROF_PARAMS;
 
     if (GC_NOTIFY) printf("start garbage_collect()\n");
@@ -2013,6 +2405,14 @@ garbage_collect(rb_objspace_t *objspace)
     }
     during_gc++;
     objspace->count++;
+    
+    if (gc_statistics) {
+        gc_collections++;
+	      gettimeofday(&gctv1, NULL);
+        if (verbose_gc_stats) {
+	    fprintf(gc_data_file, "Garbage collection started\n");
+	      }
+    }
 
     GC_PROF_TIMER_START;
     GC_PROF_MARK_TIMER_START;
@@ -2060,6 +2460,17 @@ garbage_collect(rb_objspace_t *objspace)
     GC_PROF_SWEEP_TIMER_START;
     gc_sweep(objspace);
     GC_PROF_SWEEP_TIMER_STOP;
+    
+    if (gc_statistics) {
+      GC_TIME_TYPE musecs_used;
+	    gettimeofday(&gctv2, NULL);
+	    musecs_used = ((GC_TIME_TYPE)(gctv2.tv_sec - gctv1.tv_sec) * 1000000) + (gctv2.tv_usec - gctv1.tv_usec);
+	    gc_time += musecs_used;
+
+      if (verbose_gc_stats) {
+    fprintf(gc_data_file, "GC time: %d msec\n", musecs_used / 1000);
+      }
+    }
 
     GC_PROF_TIMER_STOP;
     if (GC_NOTIFY) printf("end garbage_collect()\n");
@@ -2153,6 +2564,7 @@ Init_stack(VALUE *addr)
 void
 Init_heap(void)
 {
+    set_gc_parameters();
     init_heap(&rb_objspace);
 }
 
@@ -2868,6 +3280,34 @@ gc_profile_report(int argc, VALUE *argv, VALUE self)
     return Qnil;
 }
 
+/* call-seq:
+ *  ObjectSpace.live_objects => number
+ *
+ * Returns the count of objects currently allocated in the system. This goes
+ * down after the garbage collector runs.
+ */
+static
+VALUE os_live_objects(VALUE self)
+{ return ULONG2NUM(live_objects); }
+
+/* call-seq:
+ *  ObjectSpace.allocated_objects => number
+ *
+ * Returns the count of objects allocated since the Ruby interpreter has
+ * started.  This number can only increase. To know how many objects are
+ * currently allocated, use ObjectSpace::live_objects
+ */
+static
+VALUE os_allocated_objects(VALUE self)
+{ 
+#if defined(HAVE_LONG_LONG)
+    return ULL2NUM(allocated_objects); 
+#else
+    return ULONG2NUM(allocated_objects); 
+#endif
+}
+
+
 
 /*
  *  The <code>GC</code> module provides an interface to Ruby's mark and
@@ -2897,10 +3337,23 @@ Init_GC(void)
     rb_define_singleton_method(rb_mProfiler, "clear", gc_profile_clear, 0);
     rb_define_singleton_method(rb_mProfiler, "result", gc_profile_result, 0);
     rb_define_singleton_method(rb_mProfiler, "report", gc_profile_report, -1);
+    
+    rb_define_singleton_method(rb_mGC, "enable_stats", rb_gc_enable_stats, 0);
+    rb_define_singleton_method(rb_mGC, "disable_stats", rb_gc_disable_stats, 0);
+    rb_define_singleton_method(rb_mGC, "clear_stats", rb_gc_clear_stats, 0);
+    rb_define_singleton_method(rb_mGC, "allocated_size", rb_gc_allocated_size, 0);
+    rb_define_singleton_method(rb_mGC, "num_allocations", rb_gc_num_allocations, 0);
+    rb_define_singleton_method(rb_mGC, "collections", rb_gc_collections, 0);
+    rb_define_singleton_method(rb_mGC, "time", rb_gc_time, 0);
+    rb_define_singleton_method(rb_mGC, "dump", rb_gc_dump, 0);
+    rb_define_singleton_method(rb_mGC, "log", rb_gc_log, 1);
 
     rb_mObSpace = rb_define_module("ObjectSpace");
     rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
     rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0);
+    rb_define_module_function(rb_mObSpace, "live_objects", os_live_objects, 0);
+    rb_define_module_function(rb_mObSpace, "allocated_objects", os_allocated_objects, 0);
+
 
     rb_define_module_function(rb_mObSpace, "define_finalizer", define_final, -1);
     rb_define_module_function(rb_mObSpace, "undefine_finalizer", undefine_final, 1);
© 2025 GrazzMean