Class: Bulldogger::Probe::MethodStats
- Inherits:
-
Object
- Object
- Bulldogger::Probe::MethodStats
- Defined in:
- lib/bulldogger/probe/method_stats.rb
Overview
Aggregates every :call/:return pair observed for one probe target into the "methods"[label] shape the evidence JSON publishes: call count, per-parameter and return-value shape, the raise-exit count, and the set of call sites.
record_call/record_return take no lock: each caller writes only into its own ThreadLocal (below), reached through Thread.current -- fiber-local, like RaiseTracker's own storage, so two fibers on the same OS thread still get separate slots -- keyed uniquely to this MethodStats instance, so two threads calling the same probed method concurrently never touch the same mutable state. A per-call Mutex#synchronize was measured to be on the hot path for every single call and return; a fiber only pays a lock once -- the first time it is ever seen by this target, to register its ThreadLocal in @locals -- and #to_h folds every one of them into the published totals exactly once, at finish time.
Defined Under Namespace
Classes: ThreadLocal
Instance Method Summary collapse
-
#initialize(target:, formatter:, redactor:, max_samples:) ⇒ MethodStats
constructor
A new instance of MethodStats.
- #record_call(tp, caller_location) ⇒ Object
-
#record_return(tp, raised:) ⇒ Object
raised: true means contract-verbs.md's raise-exit discriminator fired for this return -- this call must not be counted as a nil return (that would fabricate a "returned nil" the method never actually did), so it updates the thread-local @raised instead of @returns and never touches the returns bucket at all.
- #to_h ⇒ Object
Constructor Details
#initialize(target:, formatter:, redactor:, max_samples:) ⇒ MethodStats
Returns a new instance of MethodStats.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/bulldogger/probe/method_stats.rb', line 29 def initialize(target:, formatter:, redactor:, max_samples:) @formatter = formatter @redactor = redactor @max_samples = max_samples @parameters = declared_parameters(target.unbound_method) @calls = 0 @raised_exits = 0 @param_buckets = build_param_buckets @returns = new_returns_bucket @raised = Hash.new(0) # key => [count, one representative Thread::Backtrace::Location]. # One Hash, not two: a second hash keyed the same way would be # a second lookup on every single call for no benefit -- see # #record_caller. @callers = {} # Unique per instance (not a fixed name): two targets probed # in the same session must not share one thread-local slot. @thread_key = :"bulldogger_probe_method_stats_#{object_id}" @locals = [] @locals_mutex = Mutex.new @merge_mutex = Mutex.new @merged = false end |
Instance Method Details
#record_call(tp, caller_location) ⇒ Object
55 56 57 58 59 60 |
# File 'lib/bulldogger/probe/method_stats.rb', line 55 def record_call(tp, caller_location) local = thread_local local.calls += 1 record_param_samples(local, tp.binding) record_caller(local, caller_location) end |
#record_return(tp, raised:) ⇒ Object
raised: true means contract-verbs.md's raise-exit discriminator fired for this return -- this call must not be counted as a nil return (that would fabricate a "returned nil" the method never actually did), so it updates the thread-local @raised instead of @returns and never touches the returns bucket at all.
68 69 70 71 72 73 74 75 76 77 |
# File 'lib/bulldogger/probe/method_stats.rb', line 68 def record_return(tp, raised:) local = thread_local if raised local.raised_exits += 1 klass = RaiseTracker.instance.current_exception_class_name || "Object" local.raised[klass] += 1 else local.returns.record(tp.return_value) end end |
#to_h ⇒ Object
79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/bulldogger/probe/method_stats.rb', line 79 def to_h merge! { "calls" => @calls, "raised_exits" => @raised_exits, "parameters" => @parameters, "params" => params_to_h, "returns" => @returns.to_h, "raised" => @raised, "callers" => callers_to_h } end |