memory-profiler 1.2.0 → 1.3.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 +13 -2
- data/ext/memory/profiler/capture.c +2 -2
- data/lib/memory/profiler/allocations.rb +33 -0
- data/lib/memory/profiler/call_tree.rb +25 -2
- data/lib/memory/profiler/sampler.rb +11 -19
- data/lib/memory/profiler/version.rb +1 -1
- data/lib/memory/profiler.rb +1 -0
- data/readme.md +12 -4
- data/releases.md +12 -0
- data.tar.gz.sig +0 -0
- metadata +2 -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: f059f309b7c1fd6fcffd119a0f6d40e8d4480783e2955ad3fc054fa280015f9d
|
|
4
|
+
data.tar.gz: e3c1ddd9afd7240b3bf727f6b6219b2bcb2e164c8e9c1c245ac4163684b180b8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2bd209fd9a3c9f3116bf0c76b405c1fed1580f5412292646749539f73eb5e2b7334e2267a5bbd43fbc4c13fa7c8385422e32f369779e62f022966e56108a46c3
|
|
7
|
+
data.tar.gz: 6e38c7368db453b2476a0084bbcdca8c60df5d0c51d2395df5e23c18a2b4f49f3bbbd64f5e562af2a02e91a0cd73b6cc129000eb80f75e485afbfe3b9d769ff2
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -143,13 +143,24 @@ void Memory_Profiler_Allocations_clear(VALUE allocations) {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
// Allocate a new Allocations object (for testing)
|
|
147
|
+
static VALUE Memory_Profiler_Allocations_allocate(VALUE klass) {
|
|
148
|
+
struct Memory_Profiler_Capture_Allocations *record = ALLOC(struct Memory_Profiler_Capture_Allocations);
|
|
149
|
+
record->callback = Qnil;
|
|
150
|
+
record->new_count = 0;
|
|
151
|
+
record->free_count = 0;
|
|
152
|
+
record->states = st_init_numtable();
|
|
153
|
+
|
|
154
|
+
return Memory_Profiler_Allocations_wrap(record);
|
|
155
|
+
}
|
|
156
|
+
|
|
146
157
|
void Init_Memory_Profiler_Allocations(VALUE Memory_Profiler)
|
|
147
158
|
{
|
|
148
159
|
// Allocations class - wraps allocation data for a specific class
|
|
149
160
|
Memory_Profiler_Allocations = rb_define_class_under(Memory_Profiler, "Allocations", rb_cObject);
|
|
150
161
|
|
|
151
|
-
//
|
|
152
|
-
|
|
162
|
+
// Allow allocation for testing
|
|
163
|
+
rb_define_alloc_func(Memory_Profiler_Allocations, Memory_Profiler_Allocations_allocate);
|
|
153
164
|
|
|
154
165
|
rb_define_method(Memory_Profiler_Allocations, "new_count", Memory_Profiler_Allocations_new_count, 0);
|
|
155
166
|
rb_define_method(Memory_Profiler_Allocations, "free_count", Memory_Profiler_Allocations_free_count, 0);
|
|
@@ -469,7 +469,7 @@ static VALUE Memory_Profiler_Capture_tracking_p(VALUE self, VALUE klass) {
|
|
|
469
469
|
}
|
|
470
470
|
|
|
471
471
|
// Get count of live objects for a specific class (O(1) lookup!)
|
|
472
|
-
static VALUE
|
|
472
|
+
static VALUE Memory_Profiler_Capture_retained_count_of(VALUE self, VALUE klass) {
|
|
473
473
|
struct Memory_Profiler_Capture *capture;
|
|
474
474
|
TypedData_Get_Struct(self, struct Memory_Profiler_Capture, &Memory_Profiler_Capture_type, capture);
|
|
475
475
|
|
|
@@ -641,7 +641,7 @@ void Init_Memory_Profiler_Capture(VALUE Memory_Profiler)
|
|
|
641
641
|
rb_define_method(Memory_Profiler_Capture, "track", Memory_Profiler_Capture_track, -1); // -1 to accept block
|
|
642
642
|
rb_define_method(Memory_Profiler_Capture, "untrack", Memory_Profiler_Capture_untrack, 1);
|
|
643
643
|
rb_define_method(Memory_Profiler_Capture, "tracking?", Memory_Profiler_Capture_tracking_p, 1);
|
|
644
|
-
rb_define_method(Memory_Profiler_Capture, "
|
|
644
|
+
rb_define_method(Memory_Profiler_Capture, "retained_count_of", Memory_Profiler_Capture_retained_count_of, 1);
|
|
645
645
|
rb_define_method(Memory_Profiler_Capture, "each", Memory_Profiler_Capture_each, 0);
|
|
646
646
|
rb_define_method(Memory_Profiler_Capture, "[]", Memory_Profiler_Capture_aref, 1);
|
|
647
647
|
rb_define_method(Memory_Profiler_Capture, "clear", Memory_Profiler_Capture_clear, 0);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "capture"
|
|
7
|
+
|
|
8
|
+
module Memory
|
|
9
|
+
module Profiler
|
|
10
|
+
# Ruby extensions to the C-defined Allocations class.
|
|
11
|
+
# The base Allocations class is defined in the C extension.
|
|
12
|
+
class Allocations
|
|
13
|
+
# Convert allocation statistics to JSON-compatible hash.
|
|
14
|
+
#
|
|
15
|
+
# @returns [Hash] Allocation statistics as a hash.
|
|
16
|
+
def as_json(...)
|
|
17
|
+
{
|
|
18
|
+
new_count: self.new_count,
|
|
19
|
+
free_count: self.free_count,
|
|
20
|
+
retained_count: self.retained_count,
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Convert allocation statistics to JSON string.
|
|
25
|
+
#
|
|
26
|
+
# @returns [String] Allocation statistics as JSON.
|
|
27
|
+
def to_json(...)
|
|
28
|
+
as_json.to_json(...)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
@@ -187,7 +187,7 @@ module Memory
|
|
|
187
187
|
# @parameter limit [Integer] Maximum number of paths to return.
|
|
188
188
|
# @parameter by [Symbol] Sort by :total or :retained count.
|
|
189
189
|
# @returns [Array(Array)] Array of [locations, total_count, retained_count].
|
|
190
|
-
def top_paths(limit
|
|
190
|
+
def top_paths(limit: 10, by: :retained)
|
|
191
191
|
paths = []
|
|
192
192
|
|
|
193
193
|
@root.each_path do |path, total_count, retained_count|
|
|
@@ -206,7 +206,7 @@ module Memory
|
|
|
206
206
|
# @parameter limit [Integer] Maximum number of hotspots to return.
|
|
207
207
|
# @parameter by [Symbol] Sort by :total or :retained count.
|
|
208
208
|
# @returns [Hash] Map of location => [total_count, retained_count].
|
|
209
|
-
def hotspots(limit
|
|
209
|
+
def hotspots(limit: 20, by: :retained)
|
|
210
210
|
frames = Hash.new{|h, k| h[k] = [0, 0]}
|
|
211
211
|
|
|
212
212
|
collect_frames(@root, frames)
|
|
@@ -245,6 +245,29 @@ module Memory
|
|
|
245
245
|
@root.prune!(limit)
|
|
246
246
|
end
|
|
247
247
|
|
|
248
|
+
# Convert call tree data to JSON-compatible hash.
|
|
249
|
+
#
|
|
250
|
+
# @returns [Hash] Call tree data as a hash.
|
|
251
|
+
def as_json(top_paths: {limit: 10}, hotspots: {limit: 20})
|
|
252
|
+
{
|
|
253
|
+
total_allocations: total_allocations,
|
|
254
|
+
retained_allocations: retained_allocations,
|
|
255
|
+
top_paths: top_paths(**top_paths).map{|path, total, retained|
|
|
256
|
+
{path: path, total_count: total, retained_count: retained}
|
|
257
|
+
},
|
|
258
|
+
hotspots: hotspots(**hotspots).transform_values{|total, retained|
|
|
259
|
+
{total_count: total, retained_count: retained}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Convert call tree data to JSON string.
|
|
265
|
+
#
|
|
266
|
+
# @returns [String] Call tree data as JSON.
|
|
267
|
+
def to_json(...)
|
|
268
|
+
as_json.to_json(...)
|
|
269
|
+
end
|
|
270
|
+
|
|
248
271
|
private
|
|
249
272
|
|
|
250
273
|
def collect_frames(node, frames)
|
|
@@ -7,6 +7,7 @@ require "console"
|
|
|
7
7
|
require "objspace"
|
|
8
8
|
|
|
9
9
|
require_relative "capture"
|
|
10
|
+
require_relative "allocations"
|
|
10
11
|
require_relative "call_tree"
|
|
11
12
|
|
|
12
13
|
module Memory
|
|
@@ -244,7 +245,7 @@ module Memory
|
|
|
244
245
|
|
|
245
246
|
# Get live object count for a class.
|
|
246
247
|
def count(klass)
|
|
247
|
-
@capture.
|
|
248
|
+
@capture.retained_count_of(klass)
|
|
248
249
|
end
|
|
249
250
|
|
|
250
251
|
# Get the call tree for a specific class.
|
|
@@ -256,29 +257,20 @@ module Memory
|
|
|
256
257
|
#
|
|
257
258
|
# @parameter klass [Class] The class to get statistics for.
|
|
258
259
|
# @returns [Hash] Statistics including total, retained, paths, and hotspots.
|
|
259
|
-
def
|
|
260
|
-
|
|
261
|
-
|
|
260
|
+
def analyze(klass)
|
|
261
|
+
call_tree = @call_trees[klass]
|
|
262
|
+
allocations = @capture[klass]
|
|
263
|
+
|
|
264
|
+
return nil unless call_tree or allocations
|
|
262
265
|
|
|
263
266
|
{
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
retained_allocations: tree.retained_allocations,
|
|
267
|
-
top_paths: tree.top_paths(10).map{|path, total, retained|
|
|
268
|
-
{path: path, total_count: total, retained_count: retained}
|
|
269
|
-
},
|
|
270
|
-
hotspots: tree.hotspots(20).transform_values{|total, retained|
|
|
271
|
-
{total_count: total, retained_count: retained}
|
|
272
|
-
}
|
|
267
|
+
allocations: allocations&.as_json,
|
|
268
|
+
call_tree: call_tree&.as_json
|
|
273
269
|
}
|
|
274
270
|
end
|
|
275
271
|
|
|
276
|
-
#
|
|
277
|
-
|
|
278
|
-
@call_trees.keys.each_with_object({}) do |klass, result|
|
|
279
|
-
result[klass] = statistics(klass) if tracking?(klass)
|
|
280
|
-
end
|
|
281
|
-
end
|
|
272
|
+
# @deprecated Use {analyze} instead.
|
|
273
|
+
alias statistics analyze
|
|
282
274
|
|
|
283
275
|
# Clear tracking data for a class.
|
|
284
276
|
def clear(klass)
|
data/lib/memory/profiler.rb
CHANGED
data/readme.md
CHANGED
|
@@ -22,6 +22,18 @@ 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.3.0
|
|
26
|
+
|
|
27
|
+
- **Breaking**: Renamed `Capture#count_for` to `Capture#retained_count_of` for better clarity and consistency.
|
|
28
|
+
- **Breaking**: Changed `CallTree#top_paths(limit)` to `CallTree#top_paths(limit:)` - now uses keyword argument.
|
|
29
|
+
- **Breaking**: Changed `CallTree#hotspots(limit)` to `CallTree#hotspots(limit:)` - now uses keyword argument.
|
|
30
|
+
- Simplified `Sampler#analyze` return structure to `{allocations: {...}, call_tree: {...}}` format.
|
|
31
|
+
- Added `Allocations#as_json` and `Allocations#to_json` methods for JSON serialization.
|
|
32
|
+
- Added `CallTree#as_json` and `CallTree#to_json` methods for JSON serialization with configurable options.
|
|
33
|
+
- `Memory::Profiler::Allocations.new` can now be instantiated directly (primarily for testing).
|
|
34
|
+
- `Sampler#statistics` is now a deprecated alias for `Sampler#analyze`.
|
|
35
|
+
- **Breaking**: Removed `Sampler#all_statistics` method.
|
|
36
|
+
|
|
25
37
|
### v1.2.0
|
|
26
38
|
|
|
27
39
|
- Enable custom `depth:` and `filter:` options to `Sampler#track`.
|
|
@@ -67,10 +79,6 @@ Please see the [project releases](https://socketry.github.io/memory-profiler/rel
|
|
|
67
79
|
|
|
68
80
|
- Use single global queue for event handling to avoid incorrect ordering.
|
|
69
81
|
|
|
70
|
-
### v1.1.7
|
|
71
|
-
|
|
72
|
-
- Expose `Capture#statistics` for debugging internal memory tracking state.
|
|
73
|
-
|
|
74
82
|
## Contributing
|
|
75
83
|
|
|
76
84
|
We welcome contributions to this project.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v1.3.0
|
|
4
|
+
|
|
5
|
+
- **Breaking**: Renamed `Capture#count_for` to `Capture#retained_count_of` for better clarity and consistency.
|
|
6
|
+
- **Breaking**: Changed `CallTree#top_paths(limit)` to `CallTree#top_paths(limit:)` - now uses keyword argument.
|
|
7
|
+
- **Breaking**: Changed `CallTree#hotspots(limit)` to `CallTree#hotspots(limit:)` - now uses keyword argument.
|
|
8
|
+
- Simplified `Sampler#analyze` return structure to `{allocations: {...}, call_tree: {...}}` format.
|
|
9
|
+
- Added `Allocations#as_json` and `Allocations#to_json` methods for JSON serialization.
|
|
10
|
+
- Added `CallTree#as_json` and `CallTree#to_json` methods for JSON serialization with configurable options.
|
|
11
|
+
- `Memory::Profiler::Allocations.new` can now be instantiated directly (primarily for testing).
|
|
12
|
+
- `Sampler#statistics` is now a deprecated alias for `Sampler#analyze`.
|
|
13
|
+
- **Breaking**: Removed `Sampler#all_statistics` method.
|
|
14
|
+
|
|
3
15
|
## v1.2.0
|
|
4
16
|
|
|
5
17
|
- Enable custom `depth:` and `filter:` options to `Sampler#track`.
|
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.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -56,6 +56,7 @@ files:
|
|
|
56
56
|
- ext/memory/profiler/profiler.c
|
|
57
57
|
- ext/memory/profiler/queue.h
|
|
58
58
|
- lib/memory/profiler.rb
|
|
59
|
+
- lib/memory/profiler/allocations.rb
|
|
59
60
|
- lib/memory/profiler/call_tree.rb
|
|
60
61
|
- lib/memory/profiler/capture.rb
|
|
61
62
|
- lib/memory/profiler/sampler.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|