Skip to content

Commit d138409

Browse files
Explicit track all allocations.
1 parent 8ef0202 commit d138409

File tree

5 files changed

+49
-9
lines changed

5 files changed

+49
-9
lines changed

examples/churn.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@
121121
puts
122122

123123
puts "Verification:"
124-
expected = RETAINED_COUNT
124+
# Expected count accounts for: Hash + String (hash value) + String + Array = 4 objects per iteration
125+
# So: (RETAINED_COUNT / 3) * 4 = RETAINED_COUNT * 4 / 3
126+
expected = (RETAINED_COUNT * 4 / 3).round
125127
tolerance = expected * 0.1 # Allow 10% variance due to GC timing
126128
diff = (total_live - expected).abs
127129

ext/memory/profiler/capture.c

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ struct Memory_Profiler_Capture {
2828
// Should we queue callbacks? (temporarily disabled during queue processing).
2929
int paused;
3030

31+
// Should we automatically track all classes? (if false, only explicitly tracked classes are tracked).
32+
int track_all;
33+
3134
// Tracked classes: class => VALUE (wrapped Memory_Profiler_Capture_Allocations).
3235
st_table *tracked;
3336

@@ -163,21 +166,18 @@ static void Memory_Profiler_Capture_process_newobj(VALUE self, VALUE klass, VALU
163166
// Pause the capture to prevent infinite loop:
164167
capture->paused += 1;
165168

166-
// Increment global new count:
167-
capture->new_count++;
168-
169-
// Look up or create allocations record for this class:
169+
// Look up allocations record for this class:
170170
st_data_t allocations_data;
171171
VALUE allocations;
172172
struct Memory_Profiler_Capture_Allocations *record;
173173

174174
if (st_lookup(capture->tracked, (st_data_t)klass, &allocations_data)) {
175-
// Existing record
175+
// Existing record - class is explicitly tracked
176176
allocations = (VALUE)allocations_data;
177177
record = Memory_Profiler_Allocations_get(allocations);
178178
record->new_count++;
179-
} else {
180-
// First time seeing this class, create record automatically
179+
} else if (capture->track_all) {
180+
// First time seeing this class, create record automatically (if track_all is enabled)
181181
record = ALLOC(struct Memory_Profiler_Capture_Allocations);
182182
record->callback = Qnil;
183183
record->new_count = 1;
@@ -187,8 +187,15 @@ static void Memory_Profiler_Capture_process_newobj(VALUE self, VALUE klass, VALU
187187
st_insert(capture->tracked, (st_data_t)klass, (st_data_t)allocations);
188188
RB_OBJ_WRITTEN(self, Qnil, klass);
189189
RB_OBJ_WRITTEN(self, Qnil, allocations);
190+
} else {
191+
// track_all disabled and class not explicitly tracked - skip this allocation entirely
192+
capture->paused -= 1;
193+
return;
190194
}
191195

196+
// Increment global new count (only if we're tracking this class):
197+
capture->new_count++;
198+
192199
VALUE data = Qnil;
193200
if (!NIL_P(record->callback)) {
194201
data = rb_funcall(record->callback, rb_intern("call"), 3, klass, sym_newobj, Qnil);
@@ -367,9 +374,10 @@ static VALUE Memory_Profiler_Capture_alloc(VALUE klass) {
367374
capture->new_count = 0;
368375
capture->free_count = 0;
369376

370-
// Initialize state flags - not running, callbacks disabled
377+
// Initialize state flags - not running, callbacks disabled, track_all disabled by default
371378
capture->running = 0;
372379
capture->paused = 0;
380+
capture->track_all = 0;
373381

374382
// Global event queue system will auto-initialize on first use (lazy initialization)
375383

@@ -381,6 +389,24 @@ static VALUE Memory_Profiler_Capture_initialize(VALUE self) {
381389
return self;
382390
}
383391

392+
// Get track_all setting
393+
static VALUE Memory_Profiler_Capture_track_all_get(VALUE self) {
394+
struct Memory_Profiler_Capture *capture;
395+
TypedData_Get_Struct(self, struct Memory_Profiler_Capture, &Memory_Profiler_Capture_type, capture);
396+
397+
return capture->track_all ? Qtrue : Qfalse;
398+
}
399+
400+
// Set track_all setting
401+
static VALUE Memory_Profiler_Capture_track_all_set(VALUE self, VALUE value) {
402+
struct Memory_Profiler_Capture *capture;
403+
TypedData_Get_Struct(self, struct Memory_Profiler_Capture, &Memory_Profiler_Capture_type, capture);
404+
405+
capture->track_all = RTEST(value) ? 1 : 0;
406+
407+
return value;
408+
}
409+
384410
// Start capturing allocations
385411
static VALUE Memory_Profiler_Capture_start(VALUE self) {
386412
struct Memory_Profiler_Capture *capture;
@@ -744,6 +770,8 @@ void Init_Memory_Profiler_Capture(VALUE Memory_Profiler)
744770
rb_define_alloc_func(Memory_Profiler_Capture, Memory_Profiler_Capture_alloc);
745771

746772
rb_define_method(Memory_Profiler_Capture, "initialize", Memory_Profiler_Capture_initialize, 0);
773+
rb_define_method(Memory_Profiler_Capture, "track_all", Memory_Profiler_Capture_track_all_get, 0);
774+
rb_define_method(Memory_Profiler_Capture, "track_all=", Memory_Profiler_Capture_track_all_set, 1);
747775
rb_define_method(Memory_Profiler_Capture, "start", Memory_Profiler_Capture_start, 0);
748776
rb_define_method(Memory_Profiler_Capture, "stop", Memory_Profiler_Capture_stop, 0);
749777
rb_define_method(Memory_Profiler_Capture, "track", Memory_Profiler_Capture_track, -1); // -1 to accept block

lib/memory/profiler/sampler.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def initialize(depth: 4, filter: nil, increases_threshold: 10, prune_limit: 5, p
104104
@gc = gc
105105

106106
@capture = Capture.new
107+
@capture.track_all = true
107108
@call_trees = {}
108109
@samples = {}
109110
end

test/memory/profiler/allocations.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
with "integration with Capture" do
5151
it "returns correct JSON from captured allocations" do
5252
capture = Memory::Profiler::Capture.new
53+
capture.track_all = true
5354
capture.start
5455

5556
# Create some objects

test/memory/profiler/capture.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555

5656
with "#retained_count_of" do
5757
it "tracks allocations for a class" do
58+
capture.track_all = true
5859
capture.start
5960

6061
initial_count = capture.retained_count_of(Hash)
@@ -226,6 +227,9 @@
226227
capture1 = Memory::Profiler::Capture.new
227228
capture2 = Memory::Profiler::Capture.new
228229

230+
capture1.track_all = true
231+
capture2.track_all = true
232+
229233
capture1.track(Hash)
230234
capture2.track(Array)
231235

@@ -350,6 +354,7 @@
350354

351355
with "#new_count, #free_count, #retained_count" do
352356
it "tracks total allocations across all classes" do
357+
capture.track_all = true
353358
capture.start
354359

355360
# Allocate various objects
@@ -372,6 +377,7 @@
372377
it "tracks frees when objects are collected" do
373378
GC.start
374379

380+
capture.track_all = true
375381
capture.start
376382

377383
initial_new = capture.new_count
@@ -395,6 +401,7 @@
395401
it "retained_count reflects objects still alive" do
396402
GC.start
397403

404+
capture.track_all = true
398405
capture.start
399406

400407
# Allocate and retain some objects
@@ -418,6 +425,7 @@
418425
end
419426

420427
it "counts reset after clear" do
428+
capture.track_all = true
421429
capture.start
422430

423431
20.times{Hash.new}

0 commit comments

Comments
 (0)