memory-profiler 1.3.0 → 1.4.0
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ext/memory/profiler/allocations.c +9 -70
- data/ext/memory/profiler/allocations.h +3 -7
- data/ext/memory/profiler/capture.c +279 -102
- data/ext/memory/profiler/events.c +5 -6
- data/ext/memory/profiler/events.h +4 -2
- data/lib/memory/profiler/capture.rb +1 -1
- data/lib/memory/profiler/graph.rb +369 -0
- data/lib/memory/profiler/native.rb +9 -0
- data/lib/memory/profiler/sampler.rb +62 -35
- data/lib/memory/profiler/version.rb +1 -1
- data/lib/memory/profiler.rb +1 -0
- data/readme.md +5 -4
- data/releases.md +5 -0
- data.tar.gz.sig +0 -0
- metadata +3 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cc8f56ebed50c8d591a542d81163ea09246a2661f4bd32124ea8d524f67ee940
|
|
4
|
+
data.tar.gz: 236d1bfdb3599fe908435f62a405ed6d0e21addc9cb1cac246b32e4513e55aac
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 42644bc37643d89de3e2fad38bc58fb374e5dbd213be7c1c47df4bfcf2f1ad6ffe579ae33933e69c44c8b844ff163b19511cbbade95aedd8767858deea8cb038
|
|
7
|
+
data.tar.gz: e18bffb078694246200f30b0d3199940e644eea56826dd86aef79787dfe79a96fddee09625f62c6fec88883c8ac3ae425fab38e838cded33ce98f7ae30903c70
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -2,75 +2,28 @@
|
|
|
2
2
|
// Copyright, 2025, by Samuel Williams.
|
|
3
3
|
|
|
4
4
|
#include "allocations.h"
|
|
5
|
-
|
|
6
|
-
#include
|
|
7
|
-
#include "ruby/debug.h"
|
|
8
|
-
#include "ruby/st.h"
|
|
5
|
+
#include "events.h"
|
|
6
|
+
#include <ruby/debug.h>
|
|
9
7
|
#include <stdio.h>
|
|
10
8
|
|
|
11
9
|
static VALUE Memory_Profiler_Allocations = Qnil;
|
|
12
10
|
|
|
13
|
-
// Helper to mark states table (object => state)
|
|
14
|
-
static int Memory_Profiler_Allocations_states_mark(st_data_t key, st_data_t value, st_data_t arg) {
|
|
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
|
-
|
|
18
|
-
VALUE state = (VALUE)value;
|
|
19
|
-
rb_gc_mark_movable(state);
|
|
20
|
-
return ST_CONTINUE;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Foreach callback for st_foreach_with_replace (iteration logic)
|
|
24
|
-
static int Memory_Profiler_Allocations_states_foreach(st_data_t key, st_data_t value, st_data_t argp, int error) {
|
|
25
|
-
// Return ST_REPLACE to trigger the replace callback for each entry
|
|
26
|
-
return ST_REPLACE;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Replace callback for st_foreach_with_replace to update states during compaction
|
|
30
|
-
static int Memory_Profiler_Allocations_states_compact(st_data_t *key, st_data_t *value, st_data_t data, int existing) {
|
|
31
|
-
*value = (st_data_t)rb_gc_location((VALUE)*value);
|
|
32
|
-
|
|
33
|
-
return ST_CONTINUE;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
11
|
static void Memory_Profiler_Allocations_mark(void *ptr) {
|
|
37
12
|
struct Memory_Profiler_Capture_Allocations *record = ptr;
|
|
38
13
|
|
|
39
|
-
if (!record) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
14
|
rb_gc_mark_movable(record->callback);
|
|
44
|
-
|
|
45
|
-
// Mark states table if it exists
|
|
46
|
-
if (record->states) {
|
|
47
|
-
st_foreach(record->states, Memory_Profiler_Allocations_states_mark, 0);
|
|
48
|
-
}
|
|
49
15
|
}
|
|
50
16
|
|
|
51
17
|
static void Memory_Profiler_Allocations_free(void *ptr) {
|
|
52
18
|
struct Memory_Profiler_Capture_Allocations *record = ptr;
|
|
53
19
|
|
|
54
|
-
if (record->states) {
|
|
55
|
-
st_free_table(record->states);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
20
|
xfree(record);
|
|
59
21
|
}
|
|
60
22
|
|
|
61
|
-
// GC compact function for Allocations
|
|
62
23
|
static void Memory_Profiler_Allocations_compact(void *ptr) {
|
|
63
24
|
struct Memory_Profiler_Capture_Allocations *record = ptr;
|
|
64
25
|
|
|
65
|
-
// Update callback if it moved
|
|
66
26
|
record->callback = rb_gc_location(record->callback);
|
|
67
|
-
|
|
68
|
-
// Update states table if it exists
|
|
69
|
-
if (record->states && record->states->num_entries > 0) {
|
|
70
|
-
if (st_foreach_with_replace(record->states, Memory_Profiler_Allocations_states_foreach, Memory_Profiler_Allocations_states_compact, 0)) {
|
|
71
|
-
rb_raise(rb_eRuntimeError, "states modified during GC compaction");
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
27
|
}
|
|
75
28
|
|
|
76
29
|
static const rb_data_type_t Memory_Profiler_Allocations_type = {
|
|
@@ -83,25 +36,21 @@ static const rb_data_type_t Memory_Profiler_Allocations_type = {
|
|
|
83
36
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
|
84
37
|
};
|
|
85
38
|
|
|
86
|
-
// Wrap an allocations record
|
|
87
39
|
VALUE Memory_Profiler_Allocations_wrap(struct Memory_Profiler_Capture_Allocations *record) {
|
|
88
40
|
return TypedData_Wrap_Struct(Memory_Profiler_Allocations, &Memory_Profiler_Allocations_type, record);
|
|
89
41
|
}
|
|
90
42
|
|
|
91
|
-
// Get allocations record from wrapper
|
|
92
43
|
struct Memory_Profiler_Capture_Allocations* Memory_Profiler_Allocations_get(VALUE self) {
|
|
93
44
|
struct Memory_Profiler_Capture_Allocations *record;
|
|
94
45
|
TypedData_Get_Struct(self, struct Memory_Profiler_Capture_Allocations, &Memory_Profiler_Allocations_type, record);
|
|
95
46
|
return record;
|
|
96
47
|
}
|
|
97
48
|
|
|
98
|
-
// Allocations#new_count
|
|
99
49
|
static VALUE Memory_Profiler_Allocations_new_count(VALUE self) {
|
|
100
50
|
struct Memory_Profiler_Capture_Allocations *record = Memory_Profiler_Allocations_get(self);
|
|
101
51
|
return SIZET2NUM(record->new_count);
|
|
102
52
|
}
|
|
103
53
|
|
|
104
|
-
// Allocations#free_count
|
|
105
54
|
static VALUE Memory_Profiler_Allocations_free_count(VALUE self) {
|
|
106
55
|
struct Memory_Profiler_Capture_Allocations *record = Memory_Profiler_Allocations_get(self);
|
|
107
56
|
return SIZET2NUM(record->free_count);
|
|
@@ -110,46 +59,36 @@ static VALUE Memory_Profiler_Allocations_free_count(VALUE self) {
|
|
|
110
59
|
// Allocations#retained_count
|
|
111
60
|
static VALUE Memory_Profiler_Allocations_retained_count(VALUE self) {
|
|
112
61
|
struct Memory_Profiler_Capture_Allocations *record = Memory_Profiler_Allocations_get(self);
|
|
113
|
-
|
|
62
|
+
|
|
63
|
+
// Handle underflow when free_count > new_count:
|
|
114
64
|
size_t retained = record->free_count > record->new_count ? 0 : record->new_count - record->free_count;
|
|
65
|
+
|
|
115
66
|
return SIZET2NUM(retained);
|
|
116
67
|
}
|
|
117
68
|
|
|
118
|
-
// Allocations#track { |klass| ... }
|
|
119
69
|
static VALUE Memory_Profiler_Allocations_track(int argc, VALUE *argv, VALUE self) {
|
|
120
70
|
struct Memory_Profiler_Capture_Allocations *record = Memory_Profiler_Allocations_get(self);
|
|
121
71
|
|
|
122
72
|
VALUE callback;
|
|
123
73
|
rb_scan_args(argc, argv, "&", &callback);
|
|
124
74
|
|
|
125
|
-
// Use write barrier - self (Allocations wrapper) keeps Capture alive, which keeps callback alive
|
|
126
75
|
RB_OBJ_WRITE(self, &record->callback, callback);
|
|
127
76
|
|
|
128
77
|
return self;
|
|
129
78
|
}
|
|
130
79
|
|
|
131
|
-
// Clear/reset allocation counts and state for a record
|
|
132
80
|
void Memory_Profiler_Allocations_clear(VALUE allocations) {
|
|
133
81
|
struct Memory_Profiler_Capture_Allocations *record = Memory_Profiler_Allocations_get(allocations);
|
|
134
|
-
record->new_count = 0;
|
|
135
|
-
record->free_count = 0;
|
|
136
|
-
RB_OBJ_WRITE(allocations, &record->callback, Qnil);
|
|
137
|
-
|
|
138
|
-
// Clear states - either clear the table or reinitialize
|
|
139
|
-
if (record->states) {
|
|
140
|
-
st_clear(record->states);
|
|
141
|
-
} else {
|
|
142
|
-
record->states = st_init_numtable();
|
|
143
|
-
}
|
|
82
|
+
record->new_count = 0;
|
|
83
|
+
record->free_count = 0;
|
|
84
|
+
RB_OBJ_WRITE(allocations, &record->callback, Qnil);
|
|
144
85
|
}
|
|
145
86
|
|
|
146
|
-
// Allocate a new Allocations object (for testing)
|
|
147
87
|
static VALUE Memory_Profiler_Allocations_allocate(VALUE klass) {
|
|
148
88
|
struct Memory_Profiler_Capture_Allocations *record = ALLOC(struct Memory_Profiler_Capture_Allocations);
|
|
149
89
|
record->callback = Qnil;
|
|
150
90
|
record->new_count = 0;
|
|
151
91
|
record->free_count = 0;
|
|
152
|
-
record->states = st_init_numtable();
|
|
153
92
|
|
|
154
93
|
return Memory_Profiler_Allocations_wrap(record);
|
|
155
94
|
}
|
|
@@ -165,5 +104,5 @@ void Init_Memory_Profiler_Allocations(VALUE Memory_Profiler)
|
|
|
165
104
|
rb_define_method(Memory_Profiler_Allocations, "new_count", Memory_Profiler_Allocations_new_count, 0);
|
|
166
105
|
rb_define_method(Memory_Profiler_Allocations, "free_count", Memory_Profiler_Allocations_free_count, 0);
|
|
167
106
|
rb_define_method(Memory_Profiler_Allocations, "retained_count", Memory_Profiler_Allocations_retained_count, 0);
|
|
168
|
-
rb_define_method(Memory_Profiler_Allocations, "track", Memory_Profiler_Allocations_track, -1);
|
|
107
|
+
rb_define_method(Memory_Profiler_Allocations, "track", Memory_Profiler_Allocations_track, -1);
|
|
169
108
|
}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
#pragma once
|
|
5
5
|
|
|
6
|
-
#include
|
|
7
|
-
#include
|
|
6
|
+
#include <ruby.h>
|
|
7
|
+
#include <ruby/st.h>
|
|
8
8
|
|
|
9
9
|
// Per-class allocation tracking record:
|
|
10
10
|
struct Memory_Profiler_Capture_Allocations {
|
|
@@ -16,10 +16,6 @@ struct Memory_Profiler_Capture_Allocations {
|
|
|
16
16
|
// // Total frees seen since tracking started.
|
|
17
17
|
size_t free_count;
|
|
18
18
|
// Live count = new_count - free_count.
|
|
19
|
-
|
|
20
|
-
// Map object (VALUE) => state (VALUE).
|
|
21
|
-
// Object keys need compaction updates when they move.
|
|
22
|
-
st_table *states;
|
|
23
19
|
};
|
|
24
20
|
|
|
25
21
|
// Wrap an allocations record in a VALUE.
|
|
@@ -28,7 +24,7 @@ VALUE Memory_Profiler_Allocations_wrap(struct Memory_Profiler_Capture_Allocation
|
|
|
28
24
|
// Get allocations record from wrapper VALUE.
|
|
29
25
|
struct Memory_Profiler_Capture_Allocations* Memory_Profiler_Allocations_get(VALUE self);
|
|
30
26
|
|
|
31
|
-
// Clear/reset allocation counts
|
|
27
|
+
// Clear/reset allocation counts for a record.
|
|
32
28
|
void Memory_Profiler_Allocations_clear(VALUE allocations);
|
|
33
29
|
|
|
34
30
|
// Initialize the Allocations class.
|