memory-profiler 1.1.11 → 1.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1237585b03f1c87b2c4cafb01668200bc33d518c35f832539315a7f6f37dc4f2
4
- data.tar.gz: 13a79c36183a794df061076e3e09dd2a8a1e14eadfcdb0dc5e3317a6902f622c
3
+ metadata.gz: 2cc3a03e3b76e232615954217c88c54d2077da267793f79c9b5dc5d8463398f6
4
+ data.tar.gz: 29466a48c28857d7093156c80072dc3cba66420275b307cb2fea64102ece2624
5
5
  SHA512:
6
- metadata.gz: 98c104bac6001ec5cde21d984a80a69ac992a113677ea672a81d1d1e468e754331f1170ea58c3a6ba8f1fe4a453ec669d143f7af71ab797e0d206a50bd8bbe8a
7
- data.tar.gz: 2bf799367a7cd59a760f52c89f8da0ab3ee252867bdcca431ca9340349172bcf0962f626f4cada76fcfd721084fb8de8dbe0d8692eb9593a5b4a56f625587436
6
+ metadata.gz: 9171990674d31df2af4daa7ed082b696ce1a093cb8f83ba6bab1d82ed1ef2704bb3cb4aef48758db7ba6b815e8729483d8b2124f3f687a77f3029400e1e3bb7b
7
+ data.tar.gz: 4f4845133c13fe67caa4641df912280983d05f4c6e0fca324b0827f0ffbbd040710670e9d3cd1ef6e6ab7d61bbbe22334f89ffbf4286652f4d4e361891f2dfb5
checksums.yaml.gz.sig CHANGED
Binary file
@@ -12,8 +12,9 @@ static VALUE Memory_Profiler_Allocations = Qnil;
12
12
 
13
13
  // Helper to mark states table (object => state)
14
14
  static int Memory_Profiler_Allocations_states_mark(st_data_t key, st_data_t value, st_data_t arg) {
15
- // Don't mark the object key (weak reference - don't keep objects alive)
16
- // Mark the state value
15
+ // We don't want the key to move - we can't rehash the table if it does.
16
+ rb_gc_mark((VALUE)key);
17
+
17
18
  VALUE state = (VALUE)value;
18
19
  rb_gc_mark_movable(state);
19
20
  return ST_CONTINUE;
@@ -27,19 +28,7 @@ static int Memory_Profiler_Allocations_states_foreach(st_data_t key, st_data_t v
27
28
 
28
29
  // Replace callback for st_foreach_with_replace to update states during compaction
29
30
  static int Memory_Profiler_Allocations_states_compact(st_data_t *key, st_data_t *value, st_data_t data, int existing) {
30
- // Key is object (VALUE) - we can't update if it moved (would require rehashing/allocation)
31
- VALUE old_state = (VALUE)*value;
32
- VALUE new_state = rb_gc_location(old_state);
33
-
34
- // NOTE: We can't update object keys if they moved (would require rehashing/allocation).
35
- // This means lookups may fail after compaction for moved objects.
36
- // This is acceptable - FREEOBJ will simply not find the state and skip the callback.
37
- // The state will be cleaned up when Allocations is freed/cleared.
38
-
39
- // Update state value if it moved (this is safe, doesn't rehash)
40
- if (old_state != new_state) {
41
- *value = (st_data_t)new_state;
42
- }
31
+ *value = (st_data_t)rb_gc_location((VALUE)*value);
43
32
 
44
33
  return ST_CONTINUE;
45
34
  }
@@ -302,10 +302,18 @@ static void Memory_Profiler_Capture_event_callback(VALUE data, void *ptr) {
302
302
  // Skip NEWOBJ if disabled (during callback) to prevent infinite recursion
303
303
  if (capture->paused) return;
304
304
 
305
+ // It's safe to unconditionally call here:
306
+ object = rb_obj_id(object);
307
+
305
308
  Memory_Profiler_Events_enqueue(MEMORY_PROFILER_EVENT_TYPE_NEWOBJ, data, klass, object);
306
309
  } else if (event_flag == RUBY_INTERNAL_EVENT_FREEOBJ) {
307
- // Always process FREEOBJ to ensure state cleanup
308
- Memory_Profiler_Events_enqueue(MEMORY_PROFILER_EVENT_TYPE_FREEOBJ, data, klass, object);
310
+ // We only care about objects that have been seen before (i.e. have an object ID):
311
+ if (RB_FL_TEST(object, FL_SEEN_OBJ_ID)) {
312
+ // It's only safe to call here if the object already has an object ID.
313
+ object = rb_obj_id(object);
314
+
315
+ Memory_Profiler_Events_enqueue(MEMORY_PROFILER_EVENT_TYPE_FREEOBJ, data, klass, object);
316
+ }
309
317
  }
310
318
  }
311
319
 
@@ -136,12 +136,7 @@ static void Memory_Profiler_Events_compact_queue(struct Memory_Profiler_Queue *q
136
136
  // Update all VALUEs if they moved during compaction:
137
137
  event->capture = rb_gc_location(event->capture);
138
138
  event->klass = rb_gc_location(event->klass);
139
-
140
- // For NEWOBJ: update object pointer if it moved
141
- // For FREEOBJ: DON'T update (object is being freed, pointer is stale)
142
- if (event->type == MEMORY_PROFILER_EVENT_TYPE_NEWOBJ) {
143
- event->object = rb_gc_location(event->object);
144
- }
139
+ event->object = rb_gc_location(event->object);
145
140
  }
146
141
  }
147
142
 
@@ -4,6 +4,7 @@
4
4
  # Copyright, 2025, by Samuel Williams.
5
5
 
6
6
  require "console"
7
+ require "objspace"
7
8
 
8
9
  require_relative "capture"
9
10
  require_relative "call_tree"
@@ -146,15 +147,18 @@ module Memory
146
147
  # @parameter interval [Numeric] Seconds between samples.
147
148
  # @yields {|sample| ...} Called when a class shows significant growth.
148
149
  def run(interval: 60, &block)
149
- start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
150
-
151
150
  while true
151
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
152
+
152
153
  sample!(&block)
153
154
 
155
+ # Log capture statistics to detect issues like missing FREEOBJ events:
156
+ Console.info(self, "Capture statistics:", statistics: @capture.statistics, object_space: ::ObjectSpace.count_objects)
157
+
158
+ # Sleep for the remainder of the interval:
154
159
  now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
155
160
  delta = interval - (now - start_time)
156
161
  sleep(delta) if delta > 0
157
- start_time = now
158
162
  end
159
163
  end
160
164
 
@@ -7,7 +7,7 @@
7
7
  module Memory
8
8
  # @namespace
9
9
  module Profiler
10
- VERSION = "1.1.11"
10
+ VERSION = "1.1.13"
11
11
  end
12
12
  end
13
13
 
data/readme.md CHANGED
@@ -22,6 +22,15 @@ Please see the [project documentation](https://socketry.github.io/memory-profile
22
22
 
23
23
  Please see the [project releases](https://socketry.github.io/memory-profiler/releases/index) for all releases.
24
24
 
25
+ ### v1.1.13
26
+
27
+ - Fix sampler loop interval handling.
28
+ - Log capture statistics from sampler run loop.
29
+
30
+ ### v1.1.12
31
+
32
+ - Use `rb_obj_id` for tracking object states to avoid compaction issues.
33
+
25
34
  ### v1.1.11
26
35
 
27
36
  - Double buffer shared events queues to fix queue corruption.
@@ -61,10 +70,6 @@ Please see the [project releases](https://socketry.github.io/memory-profiler/rel
61
70
 
62
71
  - Fix handling of GC compaction (I hope).
63
72
 
64
- ### v0.1.0
65
-
66
- - Initial implementation.
67
-
68
73
  ## Contributing
69
74
 
70
75
  We welcome contributions to this project.
data/releases.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Releases
2
2
 
3
+ ## v1.1.13
4
+
5
+ - Fix sampler loop interval handling.
6
+ - Log capture statistics from sampler run loop.
7
+
8
+ ## v1.1.12
9
+
10
+ - Use `rb_obj_id` for tracking object states to avoid compaction issues.
11
+
3
12
  ## v1.1.11
4
13
 
5
14
  - Double buffer shared events queues to fix queue corruption.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: memory-profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.11
4
+ version: 1.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
metadata.gz.sig CHANGED
Binary file