Class: Bulldogger::Probe::Session

Inherits:
Object
  • Object
show all
Defined in:
lib/bulldogger/probe/session.rb

Overview

One probe run: resolves and reserves its targets, builds one TracePoint per target (contract-verbs.md: "1 TracePoint can only enable one target -- can't nest-enable a targeting TracePoint"), aggregates every observed call/return into MethodStats, and writes the evidence file on finish.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(targets:, config:, run:) ⇒ Session

Returns a new instance of Session.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/bulldogger/probe/session.rb', line 62

def initialize(targets:, config:, run:)
  @targets = targets
  @config = config
  @run = run
  @redactor = Redactor.new(config.redact_patterns)
  @formatter = Formatter.new(config: config, redactor: @redactor)
  @stats = targets.each_with_object({}) do |target, hash|
    hash[target.label] = MethodStats.new(target: target, formatter: @formatter, redactor: @redactor,
                                           max_samples: config.max_samples)
  end
  @trace_points = []
  @started_at = Time.now.utc
  @finished = false
  @finish_mutex = Mutex.new
end

Class Method Details

.run(target_strings, config:, run:) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/bulldogger/probe/session.rb', line 36

def self.run(target_strings, config:, run:)
  unless config.enabled
    # The switch being off must not stop the caller's own code
    # from running -- only from being observed and written. See
    # AGENTS.md: the tool must not change what the app does.
    yield if block_given?
    return nil
  end

  session = start(target_strings, config: config, run: run)
  # The written path comes from session.finish, not from
  # `begin...ensure...end`'s own value (that would be the
  # block's return value): an ensure clause's result is
  # discarded unless captured explicitly, and finish must still
  # run -- and its path still be returned -- when the block
  # raises, since a probed run that hit an exception is exactly
  # the case where the evidence is most worth having.
  result = nil
  begin
    yield
  ensure
    result = session.finish
  end
  result
end

.start(target_strings, config:, run:) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/bulldogger/probe/session.rb', line 17

def self.start(target_strings, config:, run:)
  return nil unless config.enabled

  targets = TargetResolver.resolve!(target_strings)
  labels = targets.map(&:label)
  Registry.reserve!(labels)
  begin
    new(targets: targets, config: config, run: run).enable!
  rescue Exception # rubocop:disable Lint/RescueException
    # A target that fails mid-enable (all targets already passed
    # TargetResolver's own check, so this is the unlikely case --
    # a race with the app redefining a method, say) must not
    # leave its label stuck in Registry: that would refuse every
    # later probe of the same target for the rest of the process.
    Registry.release(labels)
    raise
  end
end

Instance Method Details

#enable!Object



78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/bulldogger/probe/session.rb', line 78

def enable!
  RaiseTracker.instance.acquire
  @targets.each do |target|
    tp = build_trace_point(@stats[target.label])
    tp.enable(target: target.unbound_method)
    @trace_points << tp
  end
  self
rescue Exception # rubocop:disable Lint/RescueException
  @trace_points.each(&:disable)
  RaiseTracker.instance.release
  raise
end

#finishObject



92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/bulldogger/probe/session.rb', line 92

def finish
  @finish_mutex.synchronize do
    return @result_path if @finished

    @finished = true
    @trace_points.each(&:disable)
    RaiseTracker.instance.release
    Registry.release(@targets.map(&:label))
    @result_path = Writer.write(run: @run, config: @config, targets: @targets, stats: @stats,
                                 started_at: @started_at)
  end
end