class OneApm::Agent::Threading::BacktraceService
Constants
- OA_ALL_TRANSACTIONS
- OA_MAX_BUFFER_LENGTH
Attributes
buffer[R]
effective_polling_period[R]
overhead_percent_threshold[R]
profile_agent_code[RW]
profiles[R]
This method is expected to be called with @lock held.
worker_loop[R]
worker_thread[RW]
Public Class Methods
is_supported?()
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 9 def self.is_supported? RUBY_VERSION >= "1.9.2" end
new(event_listener=nil)
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 18 def initialize(event_listener=nil) @profiles = {} @buffer = {} # synchronizes access to @profiles and @buffer above @lock = Mutex.new @running = false @profile_agent_code = false @worker_loop = OneApm::Support::WorkerLoop.new # Memoize overhead % to avoid getting stale OR looked up every poll @overhead_percent_threshold = OneApm::Manager.config[:'xray_session.max_profile_overhead'] OneApm::Manager.config.register_callback(:'xray_session.max_profile_overhead') do |new_value| @overhead_percent_threshold = new_value end if event_listener event_listener.subscribe(:transaction_finished, &method(:on_transaction_finished)) end end
Public Instance Methods
adjust_polling_time(now, poll_start)
click to toggle source
If our overhead % exceeds the threshold, bump the next poll period relative to how much larger our overhead is than allowed
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 227 def adjust_polling_time(now, poll_start) duration = now - poll_start overhead_percent = duration / effective_polling_period if overhead_percent > self.overhead_percent_threshold scale_up_by = overhead_percent / self.overhead_percent_threshold worker_loop.period = effective_polling_period * scale_up_by else worker_loop.period = effective_polling_period end end
aggregate_backtraces(backtraces, name, start, duration, thread)
click to toggle source
This method is expected to be called with @lock held.
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 117 def aggregate_backtraces(backtraces, name, start, duration, thread) end_time = start + duration backtraces.each do |(timestamp, backtrace)| if timestamp >= start && timestamp < end_time @profiles[name].aggregate(backtrace, :request, thread) end end end
aggregate_global_backtrace(backtrace, bucket, thread)
click to toggle source
This method is expected to be called with @lock held.
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 197 def aggregate_global_backtrace(backtrace, bucket, thread) if @profiles[OA_ALL_TRANSACTIONS] @profiles[OA_ALL_TRANSACTIONS].aggregate(backtrace, bucket, thread) end end
buffer_backtrace_for_thread(thread, timestamp, backtrace, bucket)
click to toggle source
This method is expected to be called with @lock held.
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 185 def buffer_backtrace_for_thread(thread, timestamp, backtrace, bucket) if should_buffer?(bucket) @buffer[thread] ||= [] if @buffer[thread].length < OA_MAX_BUFFER_LENGTH @buffer[thread] << [timestamp, backtrace] else OneApm::Manager.increment_metric('Supportability/XraySessions/DroppedBacktraces') end end end
effective_polling_period=(new_period)
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 145 def effective_polling_period=(new_period) @effective_polling_period = new_period self.worker_loop.period = new_period end
find_effective_polling_period()
click to toggle source
This method is expected to be called with @lock held.
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 216 def find_effective_polling_period @profiles.values.map { |p| p.requested_period }.min end
harvest(transaction_name)
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 90 def harvest(transaction_name) @lock.synchronize do if @profiles[transaction_name] profile = @profiles.delete(transaction_name) profile.finished_at = Time.now @profiles[transaction_name] = ThreadProfile.new(profile.command_arguments) profile end end end
need_backtrace?(bucket)
click to toggle source
This method is expected to be called with @lock held.
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 175 def need_backtrace?(bucket) ( bucket != :ignore && (@profiles[OA_ALL_TRANSACTIONS] || should_buffer?(bucket)) ) end
on_transaction_finished(payload)
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 101 def on_transaction_finished(payload) name = payload[:name] start = payload[:start_timestamp] duration = payload[:duration] thread = payload[:thread] || Thread.current @lock.synchronize do backtraces = @buffer.delete(thread) if backtraces && @profiles.has_key?(name) aggregate_backtraces(backtraces, name, start, duration, thread) end end end
poll()
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 150 def poll poll_start = Time.now @lock.synchronize do AgentThread.list.each do |thread| sample_thread(thread) end @profiles.each_value { |p| p.increment_poll_count } @buffer.delete_if { |thread, _| !thread.alive? } end end_time = Time.now adjust_polling_time(end_time, poll_start) record_supportability_metrics(end_time, poll_start) end
record_polling_time(now, poll_start)
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 244 def record_polling_time(now, poll_start) OneApm::Manager.record_metric('Supportability/ThreadProfiler/PollingTime', now - poll_start) end
record_skew(poll_start)
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 248 def record_skew(poll_start) if @last_poll skew = poll_start - @last_poll - worker_loop.period OneApm::Manager.record_metric('Supportability/ThreadProfiler/Skew', skew) end @last_poll = poll_start end
record_supportability_metrics(now, poll_start)
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 239 def record_supportability_metrics(now, poll_start) record_polling_time(now, poll_start) record_skew(poll_start) end
running?()
click to toggle source
Public interface
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 42 def running? @running end
sample_thread(thread)
click to toggle source
This method is expected to be called with @lock held.
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 204 def sample_thread(thread) bucket = AgentThread.bucket_thread(thread, @profile_agent_code) if need_backtrace?(bucket) timestamp = Time.now.to_f backtrace = AgentThread.scrub_backtrace(thread, @profile_agent_code) aggregate_global_backtrace(backtrace, bucket, thread) buffer_backtrace_for_thread(thread, timestamp, backtrace, bucket) end end
should_buffer?(bucket)
click to toggle source
This method is expected to be called with @lock held.
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 170 def should_buffer?(bucket) bucket == :request && @profiles.keys.any? { |k| k != OA_ALL_TRANSACTIONS } end
should_profile_agent_code?()
click to toggle source
This method is expected to be called with @lock held.
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 221 def should_profile_agent_code? @profiles.values.any? { |p| p.profile_agent_code } end
start()
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 126 def start return if @running || !self.class.is_supported? @running = true self.worker_thread = AgentThread.create('Backtrace Service') do # Not passing period because we expect it's already been set. self.worker_loop.run(&method(:poll)) end end
stop()
click to toggle source
This method is expected to be called with @lock held
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 137 def stop return unless @running @running = false self.worker_loop.stop @buffer = {} end
subscribe(transaction_name, command_arguments={})
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 46 def subscribe(transaction_name, command_arguments={}) if !self.class.is_supported? OneApm::Manager.logger.debug("Backtracing not supported, so not subscribing transaction '#{transaction_name}'") return end OneApm::Manager.logger.debug("Backtrace Service subscribing transaction '#{transaction_name}'") profile = ThreadProfile.new(command_arguments) @lock.synchronize do @profiles[transaction_name] = profile update_values_from_profiles end start profile end
subscribed?(transaction_name)
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 84 def subscribed?(transaction_name) @lock.synchronize do @profiles.has_key?(transaction_name) end end
unsubscribe(transaction_name)
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 65 def unsubscribe(transaction_name) return unless self.class.is_supported? OneApm::Manager.logger.debug("Backtrace Service unsubscribing transaction '#{transaction_name}'") @lock.synchronize do @profiles.delete(transaction_name) if @profiles.empty? stop else update_values_from_profiles end end end
update_values_from_profiles()
click to toggle source
# File lib/one_apm/support/backtrace/backtrace_service.rb, line 79 def update_values_from_profiles self.effective_polling_period = find_effective_polling_period self.profile_agent_code = should_profile_agent_code? end