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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44131407c83cd30a000572173db2bd27a1b2cc226090fe5b2ab5b9452671676a
4
- data.tar.gz: 76c488be0ca9902ac543a21941b3ebd4d5fd5ea807e958289452044021ba8f0c
3
+ metadata.gz: f059f309b7c1fd6fcffd119a0f6d40e8d4480783e2955ad3fc054fa280015f9d
4
+ data.tar.gz: e3c1ddd9afd7240b3bf727f6b6219b2bcb2e164c8e9c1c245ac4163684b180b8
5
5
  SHA512:
6
- metadata.gz: 98eba2b7e667ad5d9e9ea01a528de98a4525074580e66178b91f246932183557d0a826a8693e773147977e694f7ef9a15af47d0d8f954c4352bb77ada9d5e558
7
- data.tar.gz: 71424607c4a188ef16da5436c4bd7b0ebc6640b3bbc1b182f0a9cc64c15b940bb4cf81a3f1cc47f00cfb5b7dc596565a3f22df88cd6c9ccb80b7866122c6bb53
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
- // Allocations objects are only created internally via wrap, never from Ruby:
152
- rb_undef_alloc_func(Memory_Profiler_Allocations);
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 Memory_Profiler_Capture_count_for(VALUE self, VALUE klass) {
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, "count_for", Memory_Profiler_Capture_count_for, 1);
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 = 10, by: :retained)
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 = 20, by: :retained)
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.count_for(klass)
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 statistics(klass)
260
- tree = @call_trees[klass]
261
- return nil unless tree
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
- live_count: @capture.count_for(klass),
265
- total_allocations: tree.total_allocations,
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
- # Get statistics for all tracked classes.
277
- def all_statistics
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)
@@ -7,7 +7,7 @@
7
7
  module Memory
8
8
  # @namespace
9
9
  module Profiler
10
- VERSION = "1.2.0"
10
+ VERSION = "1.3.0"
11
11
  end
12
12
  end
13
13
 
@@ -6,4 +6,5 @@
6
6
  require_relative "profiler/version"
7
7
  require_relative "profiler/call_tree"
8
8
  require_relative "profiler/capture"
9
+ require_relative "profiler/allocations"
9
10
  require_relative "profiler/sampler"
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.2.0
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