dead_bro 0.2.35 → 0.2.36
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
- data/CHANGELOG.md +6 -0
- data/lib/dead_bro/gc_tracker.rb +24 -5
- data/lib/dead_bro/job_subscriber.rb +1 -0
- data/lib/dead_bro/memory_leak_detector.rb +2 -1
- data/lib/dead_bro/subscriber.rb +8 -4
- data/lib/dead_bro/version.rb +2 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7f33da7799522248140cc65b3637ae2a1dede4295b3696370bd3aabea0d09c7b
|
|
4
|
+
data.tar.gz: 376071ecd3eafe0e41695246074ccea3b3a2c479532d08eb24d46fa0d471c94e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba9539bb294999bcd1d3b1f4bed9bc7612d4b9f3e66fe92769de92a0b50d8991ffadf2daf6e418b3951fcda7b8e1ae9afa5b6980c4de7af49d982ccd07aff6f2
|
|
7
|
+
data.tar.gz: 1e9abbd0ef17016ea6f639fa60d6d18edc0b481cdeec3d6495f52f5ae31e73a413db0bb7e7607b274a344d8ab1b33dda098dca608c617a024622aff3796fc8e0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
### Added
|
|
4
|
+
- **`gc_pressure` now says whether the window's GC had finished.** `gc_state_at_end` is the GC's phase when a request or job window closes — `"none"`, `"marking"` or `"sweeping"`, from `GC.latest_gc_info(:state)` — sent with the other memory-tracking fields. Ruby marks and sweeps in steps between allocations, so a GC counted in the window is often still running when it closes, and until it finishes `heap_live_slots_growth` still counts garbage it hasn't freed. The dashboard uses it so a sample measured mid-GC isn't flagged as a possible leak. It's a single-key read; no hash is built.
|
|
5
|
+
|
|
6
|
+
### Fixed
|
|
7
|
+
- **The in-process leak detector's `object_count` was always 0.** `gc_stats` never read `heap_live_slots`, so every `MemoryLeakDetector` sample recorded `object_count: 0` and the `object_stats` / `memory_efficiency` figures from `DeadBro::MemoryHelpers.analyze_memory` were meaningless. `gc_stats` now includes `heap_live_slots` — in request and job payloads alike — and the request's leak sample and payload share one `GC.stat` read. `average_memory_per_object_kb` is now actually in KB; it divided RSS in MB by the object count.
|
|
8
|
+
|
|
3
9
|
## [0.2.35] - 2026-10-21
|
|
4
10
|
|
|
5
11
|
### Fixed
|
data/lib/dead_bro/gc_tracker.rb
CHANGED
|
@@ -29,14 +29,17 @@ module DeadBro
|
|
|
29
29
|
# Memory-tracking enrichment (a few extra GC.stat reads). Only the base
|
|
30
30
|
# GC pressure fields above are truly always-on.
|
|
31
31
|
if memory_tracking_enabled?
|
|
32
|
-
# Live heap slots
|
|
33
|
-
#
|
|
32
|
+
# Live heap slots, process-wide. How to read their delta is explained
|
|
33
|
+
# in self.diff below.
|
|
34
34
|
base[:heap_live_slots] = stat[:heap_live_slots] || 0
|
|
35
35
|
# Bytes malloc'd outside the Ruby object heap (big strings/buffers, e.g.
|
|
36
36
|
# parsed JSON response bodies). These are point-in-time gauges reset by
|
|
37
37
|
# GC, so we report the request-end value rather than a diff.
|
|
38
38
|
base[:malloc_increase_bytes] = stat[:malloc_increase_bytes] || 0
|
|
39
39
|
base[:oldmalloc_increase_bytes] = stat[:oldmalloc_increase_bytes] || 0
|
|
40
|
+
# The GC's phase right after those counters were read: :none, :marking
|
|
41
|
+
# or :sweeping. See self.diff below.
|
|
42
|
+
base[:gc_state] = gc_state
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
base
|
|
@@ -44,6 +47,13 @@ module DeadBro
|
|
|
44
47
|
{}
|
|
45
48
|
end
|
|
46
49
|
|
|
50
|
+
# A single-key read: no hash is built. nil where the VM doesn't report it.
|
|
51
|
+
def self.gc_state
|
|
52
|
+
GC.latest_gc_info(:state) if GC.respond_to?(:latest_gc_info)
|
|
53
|
+
rescue
|
|
54
|
+
nil
|
|
55
|
+
end
|
|
56
|
+
|
|
47
57
|
def self.diff(before, after)
|
|
48
58
|
return {} if before.empty? || after.empty?
|
|
49
59
|
gc_time_ms = if before[:gc_time_ns] && after[:gc_time_ns]
|
|
@@ -58,10 +68,19 @@ module DeadBro
|
|
|
58
68
|
|
|
59
69
|
# Present only when the enrichment was captured (memory tracking enabled).
|
|
60
70
|
if after.key?(:heap_live_slots) || before.key?(:heap_live_slots)
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
71
|
+
# Signed net change in live slots over the request. Like allocated_objects
|
|
72
|
+
# it is process-wide: every thread's allocations and frees count. It only
|
|
73
|
+
# separates transient churn from retention once a GC in the window has
|
|
74
|
+
# finished: without one, slots are freed only by the lazy sweep of an
|
|
75
|
+
# earlier GC, and a GC still running when the window closes hasn't freed
|
|
76
|
+
# all its garbage yet. It goes negative when more is freed than
|
|
77
|
+
# allocated. Read it together with minor_gc_runs/major_gc_runs and
|
|
78
|
+
# gc_state_at_end.
|
|
64
79
|
result[:heap_live_slots_growth] = (after[:heap_live_slots] || 0) - (before[:heap_live_slots] || 0)
|
|
80
|
+
# The GC's phase when the window closed: "none" once the last GC has
|
|
81
|
+
# finished, "marking" or "sweeping" while it's still running (Ruby marks
|
|
82
|
+
# and sweeps in steps between allocations).
|
|
83
|
+
result[:gc_state_at_end] = after[:gc_state].to_s if after[:gc_state]
|
|
65
84
|
# Off-heap malloc pressure pending at request end (see snapshot).
|
|
66
85
|
result[:malloc_increase_bytes] = after[:malloc_increase_bytes] || 0
|
|
67
86
|
result[:oldmalloc_increase_bytes] = after[:oldmalloc_increase_bytes] || 0
|
|
@@ -307,6 +307,7 @@ module DeadBro
|
|
|
307
307
|
count: stats[:count] || 0,
|
|
308
308
|
heap_allocated_pages: stats[:heap_allocated_pages] || 0,
|
|
309
309
|
heap_sorted_pages: stats[:heap_sorted_pages] || 0,
|
|
310
|
+
heap_live_slots: stats[:heap_live_slots] || 0,
|
|
310
311
|
total_allocated_objects: stats[:total_allocated_objects] || 0
|
|
311
312
|
}
|
|
312
313
|
else
|
|
@@ -141,8 +141,9 @@ module DeadBro
|
|
|
141
141
|
def calculate_memory_efficiency(samples)
|
|
142
142
|
return {} if samples.length < 2
|
|
143
143
|
|
|
144
|
+
# memory_usage is RSS in MB; object_count is live heap slots.
|
|
144
145
|
memory_per_object = samples.map do |sample|
|
|
145
|
-
(sample[:object_count] > 0) ? sample[:memory_usage] / sample[:object_count] : 0
|
|
146
|
+
(sample[:object_count] > 0) ? sample[:memory_usage] * 1024.0 / sample[:object_count] : 0
|
|
146
147
|
end
|
|
147
148
|
|
|
148
149
|
gc_efficiency = []
|
data/lib/dead_bro/subscriber.rb
CHANGED
|
@@ -139,13 +139,16 @@ module DeadBro
|
|
|
139
139
|
}
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
+
# One GC.stat read for both the leak-detection sample and the payload.
|
|
143
|
+
end_gc_stats = gc_stats
|
|
144
|
+
|
|
142
145
|
# Record memory sample for leak detection (only if memory tracking enabled)
|
|
143
146
|
if DeadBro.configuration.memory_tracking_enabled
|
|
144
147
|
DeadBro::MemoryLeakDetector.record_memory_sample({
|
|
145
148
|
memory_usage: memory_usage_mb,
|
|
146
|
-
gc_count:
|
|
147
|
-
heap_pages:
|
|
148
|
-
object_count:
|
|
149
|
+
gc_count: end_gc_stats[:count],
|
|
150
|
+
heap_pages: end_gc_stats[:heap_allocated_pages],
|
|
151
|
+
object_count: end_gc_stats[:heap_live_slots],
|
|
149
152
|
request_id: data[:request_id],
|
|
150
153
|
controller: data[:controller],
|
|
151
154
|
action: data[:action]
|
|
@@ -162,7 +165,7 @@ module DeadBro
|
|
|
162
165
|
view_runtime_ms: data[:view_runtime],
|
|
163
166
|
db_runtime_ms: data[:db_runtime],
|
|
164
167
|
memory_usage: memory_usage_mb,
|
|
165
|
-
gc_stats:
|
|
168
|
+
gc_stats: end_gc_stats,
|
|
166
169
|
sql_count: sql_count(data),
|
|
167
170
|
sql_queries: sql_queries,
|
|
168
171
|
transaction_events: transaction_events,
|
|
@@ -459,6 +462,7 @@ module DeadBro
|
|
|
459
462
|
count: stats[:count] || 0,
|
|
460
463
|
heap_allocated_pages: stats[:heap_allocated_pages] || 0,
|
|
461
464
|
heap_sorted_pages: stats[:heap_sorted_pages] || 0,
|
|
465
|
+
heap_live_slots: stats[:heap_live_slots] || 0,
|
|
462
466
|
total_allocated_objects: stats[:total_allocated_objects] || 0
|
|
463
467
|
}
|
|
464
468
|
else
|
data/lib/dead_bro/version.rb
CHANGED