memory-tracker-x 0.1.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 +7 -0
- checksums.yaml.gz.sig +3 -0
- data/context/getting-started.md +229 -0
- data/context/index.yaml +12 -0
- data/ext/extconf.rb +37 -0
- data/ext/memory/tracker/capture.c +510 -0
- data/ext/memory/tracker/capture.h +9 -0
- data/ext/memory/tracker/tracker.c +17 -0
- data/lib/memory/tracker/call_tree.rb +138 -0
- data/lib/memory/tracker/capture.rb +6 -0
- data/lib/memory/tracker/sampler.rb +270 -0
- data/lib/memory/tracker/version.rb +13 -0
- data/lib/memory/tracker.rb +9 -0
- data/license.md +21 -0
- data/readme.md +45 -0
- data/releases.md +5 -0
- data.tar.gz.sig +0 -0
- metadata +83 -0
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "capture"
|
|
7
|
+
require_relative "call_tree"
|
|
8
|
+
|
|
9
|
+
module Memory
|
|
10
|
+
module Tracker
|
|
11
|
+
# Periodic sampler for monitoring memory growth over time.
|
|
12
|
+
#
|
|
13
|
+
# Samples class allocation counts at regular intervals and detects potential memory leaks
|
|
14
|
+
# by tracking when counts increase beyond a threshold. When a class exceeds the increases
|
|
15
|
+
# threshold, automatically enables detailed call path tracking for diagnosis.
|
|
16
|
+
class Sampler
|
|
17
|
+
# Tracks memory growth for a specific class.
|
|
18
|
+
#
|
|
19
|
+
# Records allocation counts over time and detects sustained growth patterns
|
|
20
|
+
# that indicate potential memory leaks.
|
|
21
|
+
class Sample
|
|
22
|
+
# Create a new sample tracker for a class.
|
|
23
|
+
#
|
|
24
|
+
# @parameter target [Class] The class being sampled.
|
|
25
|
+
# @parameter size [Integer] Initial object count.
|
|
26
|
+
# @parameter threshold [Integer] Minimum increase to consider significant.
|
|
27
|
+
def initialize(target, size = 0, threshold: 1000)
|
|
28
|
+
@target = target
|
|
29
|
+
@current_size = size
|
|
30
|
+
@maximum_observed_size = size
|
|
31
|
+
@threshold = threshold
|
|
32
|
+
|
|
33
|
+
@sample_count = 0
|
|
34
|
+
@increases = 0
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
attr_reader :target, :current_size, :maximum_observed_size, :threshold, :sample_count, :increases
|
|
38
|
+
|
|
39
|
+
# Record a new sample measurement.
|
|
40
|
+
#
|
|
41
|
+
# @parameter size [Integer] Current object count for this class.
|
|
42
|
+
# @returns [Boolean] True if count increased significantly.
|
|
43
|
+
def sample!(size)
|
|
44
|
+
@sample_count += 1
|
|
45
|
+
@current_size = size
|
|
46
|
+
|
|
47
|
+
# @maximum_observed_count ratchets up in units of at least @threshold counts.
|
|
48
|
+
# When it does, we bump @increases to track a potential memory leak.
|
|
49
|
+
if @maximum_observed_size
|
|
50
|
+
delta = @current_size - @maximum_observed_size
|
|
51
|
+
if delta > @threshold
|
|
52
|
+
@maximum_observed_size = size
|
|
53
|
+
@increases += 1
|
|
54
|
+
|
|
55
|
+
return true
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
@maximum_observed_size = size
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
return false
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Convert sample data to JSON-compatible hash.
|
|
65
|
+
#
|
|
66
|
+
# @returns [Hash] Sample data as a hash.
|
|
67
|
+
def as_json(...)
|
|
68
|
+
{
|
|
69
|
+
target: @target.name || "(anonymous class)",
|
|
70
|
+
current_size: @current_size,
|
|
71
|
+
maximum_observed_size: @maximum_observed_size,
|
|
72
|
+
increases: @increases,
|
|
73
|
+
sample_count: @sample_count,
|
|
74
|
+
threshold: @threshold,
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Convert sample data to JSON string.
|
|
79
|
+
#
|
|
80
|
+
# @returns [String] Sample data as JSON.
|
|
81
|
+
def to_json(...)
|
|
82
|
+
as_json.to_json(...)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
attr_reader :depth
|
|
87
|
+
|
|
88
|
+
# Create a new memory sampler.
|
|
89
|
+
#
|
|
90
|
+
# @parameter depth [Integer] Number of stack frames to capture for call path analysis.
|
|
91
|
+
# @parameter filter [Proc] Optional filter to exclude frames from call paths.
|
|
92
|
+
# @parameter increases_threshold [Integer] Number of increases before enabling detailed tracking.
|
|
93
|
+
def initialize(depth: 10, filter: nil, increases_threshold: 10)
|
|
94
|
+
@depth = depth
|
|
95
|
+
@filter = filter || default_filter
|
|
96
|
+
@increases_threshold = increases_threshold
|
|
97
|
+
@capture = Capture.new
|
|
98
|
+
@call_trees = {}
|
|
99
|
+
@samples = {}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Start capturing allocations.
|
|
103
|
+
def start
|
|
104
|
+
@capture.start
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Stop capturing allocations.
|
|
108
|
+
def stop
|
|
109
|
+
@capture.stop
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Run periodic sampling in a loop.
|
|
113
|
+
#
|
|
114
|
+
# Samples allocation counts at the specified interval and reports when
|
|
115
|
+
# classes show sustained memory growth. Automatically tracks ALL classes
|
|
116
|
+
# that allocate objects - no need to specify them upfront.
|
|
117
|
+
#
|
|
118
|
+
# @parameter interval [Numeric] Seconds between samples.
|
|
119
|
+
# @yields {|sample| ...} Called when a class shows significant growth.
|
|
120
|
+
def run(interval: 60, &block)
|
|
121
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
122
|
+
|
|
123
|
+
while true
|
|
124
|
+
sample!(&block)
|
|
125
|
+
|
|
126
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
127
|
+
delta = interval - (now - start_time)
|
|
128
|
+
sleep(delta) if delta > 0
|
|
129
|
+
start_time = now
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Take a single sample of memory usage for all tracked classes.
|
|
134
|
+
#
|
|
135
|
+
# @yields {|sample| ...} Called when a class shows significant growth.
|
|
136
|
+
def sample!
|
|
137
|
+
@capture.each do |klass, allocations|
|
|
138
|
+
count = allocations.retained_count
|
|
139
|
+
sample = @samples[klass] ||= Sample.new(klass, count)
|
|
140
|
+
|
|
141
|
+
if sample.sample!(count)
|
|
142
|
+
# Check if we should enable detailed tracking
|
|
143
|
+
if sample.increases >= @increases_threshold && !@call_trees.key?(klass)
|
|
144
|
+
track_with_analysis_internal(klass, allocations)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Notify about growth if block given
|
|
148
|
+
yield sample if block_given?
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Internal: Enable tracking with analysis using allocations object
|
|
154
|
+
private def track_with_analysis_internal(klass, allocations)
|
|
155
|
+
tree = @call_trees[klass] = CallTree.new
|
|
156
|
+
depth = @depth
|
|
157
|
+
filter = @filter
|
|
158
|
+
|
|
159
|
+
# Register callback on allocations object
|
|
160
|
+
allocations.track do |obj_klass|
|
|
161
|
+
# Callback captures caller_locations with desired depth
|
|
162
|
+
locations = caller_locations(1, depth)
|
|
163
|
+
filtered = locations.select(&filter)
|
|
164
|
+
tree.record(filtered) unless filtered.empty?
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Start tracking allocations for a class (count only).
|
|
169
|
+
def track(klass)
|
|
170
|
+
return if @capture.tracking?(klass)
|
|
171
|
+
|
|
172
|
+
@capture.track(klass)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Start tracking with call path analysis.
|
|
176
|
+
#
|
|
177
|
+
# @parameter klass [Class] The class to track with detailed analysis.
|
|
178
|
+
def track_with_analysis(klass)
|
|
179
|
+
# Track the class if not already tracked
|
|
180
|
+
unless @capture.tracking?(klass)
|
|
181
|
+
@capture.track(klass)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Enable analysis by setting callback on the allocations object
|
|
185
|
+
@capture.each do |tracked_klass, allocations|
|
|
186
|
+
if tracked_klass == klass
|
|
187
|
+
track_with_analysis_internal(klass, allocations)
|
|
188
|
+
break
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Stop tracking a specific class.
|
|
194
|
+
def untrack(klass)
|
|
195
|
+
@capture.untrack(klass)
|
|
196
|
+
@call_trees.delete(klass)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Check if a class is being tracked.
|
|
200
|
+
def tracking?(klass)
|
|
201
|
+
@capture.tracking?(klass)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Get live object count for a class.
|
|
205
|
+
def count(klass)
|
|
206
|
+
@capture.count_for(klass)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Get the call tree for a specific class.
|
|
210
|
+
def call_tree(klass)
|
|
211
|
+
@call_trees[klass]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Get allocation statistics for a tracked class.
|
|
215
|
+
def statistics(klass)
|
|
216
|
+
tree = @call_trees[klass]
|
|
217
|
+
return nil unless tree
|
|
218
|
+
|
|
219
|
+
{
|
|
220
|
+
live_count: @capture.count_for(klass),
|
|
221
|
+
total_allocations: tree.total_allocations,
|
|
222
|
+
top_paths: tree.top_paths(10).map {|path, count|
|
|
223
|
+
{ path: path, count: count }
|
|
224
|
+
},
|
|
225
|
+
hotspots: tree.hotspots(20)
|
|
226
|
+
}
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Get statistics for all tracked classes.
|
|
230
|
+
def all_statistics
|
|
231
|
+
@call_trees.keys.each_with_object({}) do |klass, result|
|
|
232
|
+
result[klass] = statistics(klass) if tracking?(klass)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Clear tracking data for a class.
|
|
237
|
+
def clear(klass)
|
|
238
|
+
tree = @call_trees[klass]
|
|
239
|
+
tree&.clear!
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Clear all tracking data.
|
|
243
|
+
def clear_all!
|
|
244
|
+
@call_trees.each_value(&:clear!)
|
|
245
|
+
@capture.clear
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Stop all tracking and clean up.
|
|
249
|
+
def stop!
|
|
250
|
+
@capture.stop
|
|
251
|
+
@call_trees.each_key do |klass|
|
|
252
|
+
@capture.untrack(klass)
|
|
253
|
+
end
|
|
254
|
+
@capture.clear
|
|
255
|
+
@call_trees.clear
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
private
|
|
259
|
+
|
|
260
|
+
def default_filter
|
|
261
|
+
->(location) {path = location.path
|
|
262
|
+
!path.include?("/gems/") &&
|
|
263
|
+
!path.include?("/ruby/") &&
|
|
264
|
+
!path.start_with?("(eval)")
|
|
265
|
+
}
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
data/license.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright, 2025, by Samuel Williams.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/readme.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Memory::Tracker
|
|
2
|
+
|
|
3
|
+
Efficient memory allocation tracking focused on **retained objects only**. Automatically tracks allocations and cleans up when objects are freed, giving you precise data on memory leaks.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/socketry/memory-tracker/actions?workflow=Test)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Retained Objects Only**: Uses `RUBY_INTERNAL_EVENT_NEWOBJ` and `RUBY_INTERNAL_EVENT_FREEOBJ` to automatically track only objects that survive GC.
|
|
10
|
+
- **O(1) Live Counts**: Maintains per-class counters updated on alloc/free - no heap enumeration needed\!
|
|
11
|
+
- **Tree-Based Analysis**: Deduplicates common call paths using an efficient tree structure.
|
|
12
|
+
- **Native C Extension**: **Required** - uses Ruby internal events not available in pure Ruby.
|
|
13
|
+
- **Configurable Depth**: Control how deep to capture call stacks.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Please see the [project documentation](https://socketry.github.io/memory-tracker/) for more details.
|
|
18
|
+
|
|
19
|
+
- [Getting Started](https://socketry.github.io/memory-tracker/guides/getting-started/index) - This guide explains how to use `memory-tracker` to detect and diagnose memory leaks in Ruby applications.
|
|
20
|
+
|
|
21
|
+
## Releases
|
|
22
|
+
|
|
23
|
+
Please see the [project releases](https://socketry.github.io/memory-tracker/releases/index) for all releases.
|
|
24
|
+
|
|
25
|
+
### v0.1.0
|
|
26
|
+
|
|
27
|
+
- Initial implementation.
|
|
28
|
+
|
|
29
|
+
## Contributing
|
|
30
|
+
|
|
31
|
+
We welcome contributions to this project.
|
|
32
|
+
|
|
33
|
+
1. Fork it.
|
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`).
|
|
35
|
+
3. Commit your changes (`git commit -am 'Add some feature'`).
|
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`).
|
|
37
|
+
5. Create new Pull Request.
|
|
38
|
+
|
|
39
|
+
### Developer Certificate of Origin
|
|
40
|
+
|
|
41
|
+
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
|
42
|
+
|
|
43
|
+
### Community Guidelines
|
|
44
|
+
|
|
45
|
+
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
data/releases.md
ADDED
data.tar.gz.sig
ADDED
|
Binary file
|
metadata
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: memory-tracker-x
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Samuel Williams
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain:
|
|
10
|
+
- |
|
|
11
|
+
-----BEGIN CERTIFICATE-----
|
|
12
|
+
MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
|
|
13
|
+
ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
|
|
14
|
+
CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
|
|
15
|
+
MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
|
|
16
|
+
MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
|
|
17
|
+
bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
|
|
18
|
+
igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
|
|
19
|
+
9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
|
|
20
|
+
sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
|
|
21
|
+
e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
|
|
22
|
+
XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
|
|
23
|
+
RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
|
|
24
|
+
tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
|
|
25
|
+
zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
|
|
26
|
+
xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
|
|
27
|
+
BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
|
|
28
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
|
|
29
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
|
|
30
|
+
cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
|
|
31
|
+
xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
|
|
32
|
+
c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
|
|
33
|
+
8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
|
|
34
|
+
JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
|
|
35
|
+
eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
|
|
36
|
+
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
|
37
|
+
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
|
38
|
+
-----END CERTIFICATE-----
|
|
39
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
40
|
+
dependencies: []
|
|
41
|
+
executables: []
|
|
42
|
+
extensions:
|
|
43
|
+
- ext/extconf.rb
|
|
44
|
+
extra_rdoc_files: []
|
|
45
|
+
files:
|
|
46
|
+
- context/getting-started.md
|
|
47
|
+
- context/index.yaml
|
|
48
|
+
- ext/extconf.rb
|
|
49
|
+
- ext/memory/tracker/capture.c
|
|
50
|
+
- ext/memory/tracker/capture.h
|
|
51
|
+
- ext/memory/tracker/tracker.c
|
|
52
|
+
- lib/memory/tracker.rb
|
|
53
|
+
- lib/memory/tracker/call_tree.rb
|
|
54
|
+
- lib/memory/tracker/capture.rb
|
|
55
|
+
- lib/memory/tracker/sampler.rb
|
|
56
|
+
- lib/memory/tracker/version.rb
|
|
57
|
+
- license.md
|
|
58
|
+
- readme.md
|
|
59
|
+
- releases.md
|
|
60
|
+
homepage: https://github.com/socketry/memory-tracker
|
|
61
|
+
licenses:
|
|
62
|
+
- MIT
|
|
63
|
+
metadata:
|
|
64
|
+
documentation_uri: https://socketry.github.io/memory-tracker/
|
|
65
|
+
source_code_uri: https://github.com/socketry/memory-tracker
|
|
66
|
+
rdoc_options: []
|
|
67
|
+
require_paths:
|
|
68
|
+
- lib
|
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '3.2'
|
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
|
+
requirements:
|
|
76
|
+
- - ">="
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: '0'
|
|
79
|
+
requirements: []
|
|
80
|
+
rubygems_version: 3.6.9
|
|
81
|
+
specification_version: 4
|
|
82
|
+
summary: Efficient memory allocation tracking with call path analysis.
|
|
83
|
+
test_files: []
|
metadata.gz.sig
ADDED
|
Binary file
|