oneapm_rpm 1.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.
Files changed (234) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +30 -0
  3. data/.rubocop.yml +725 -0
  4. data/Gemfile +3 -0
  5. data/Guardfile +7 -0
  6. data/LICENSE +1 -0
  7. data/README.md +3 -0
  8. data/config/cert/cacert.pem +1177 -0
  9. data/config/database.yml +5 -0
  10. data/lib/initializers/goliath.rb +11 -0
  11. data/lib/initializers/other.rb +1 -0
  12. data/lib/initializers/rails.rb +15 -0
  13. data/lib/one_apm/agent.rb +253 -0
  14. data/lib/one_apm/agent/agent.rb +283 -0
  15. data/lib/one_apm/agent/agent/connect.rb +175 -0
  16. data/lib/one_apm/agent/agent/container_data_manager.rb +218 -0
  17. data/lib/one_apm/agent/agent/forkable_dispatcher_functions.rb +96 -0
  18. data/lib/one_apm/agent/agent/helpers.rb +45 -0
  19. data/lib/one_apm/agent/agent/start.rb +226 -0
  20. data/lib/one_apm/agent/agent/start_worker_thread.rb +148 -0
  21. data/lib/one_apm/agent/busy_calculator.rb +115 -0
  22. data/lib/one_apm/agent/cross_app/cross_app_monitor.rb +181 -0
  23. data/lib/one_apm/agent/cross_app/cross_app_tracing.rb +336 -0
  24. data/lib/one_apm/agent/database.rb +308 -0
  25. data/lib/one_apm/agent/database/active_record_helper.rb +80 -0
  26. data/lib/one_apm/agent/database/obfuscation_helpers.rb +76 -0
  27. data/lib/one_apm/agent/database/obfuscator.rb +78 -0
  28. data/lib/one_apm/agent/database/postgres_explain_obfuscator.rb +45 -0
  29. data/lib/one_apm/agent/datastores.rb +175 -0
  30. data/lib/one_apm/agent/datastores/metric_helper.rb +83 -0
  31. data/lib/one_apm/agent/datastores/mongo.rb +27 -0
  32. data/lib/one_apm/agent/datastores/mongo/metric_translator.rb +189 -0
  33. data/lib/one_apm/agent/datastores/mongo/obfuscator.rb +37 -0
  34. data/lib/one_apm/agent/datastores/mongo/statement_formatter.rb +51 -0
  35. data/lib/one_apm/agent/event/event_listener.rb +40 -0
  36. data/lib/one_apm/agent/event/event_loop.rb +191 -0
  37. data/lib/one_apm/agent/event/worker_loop.rb +97 -0
  38. data/lib/one_apm/agent/harvester.rb +48 -0
  39. data/lib/one_apm/agent/inbound_request_monitor.rb +30 -0
  40. data/lib/one_apm/agent/javascript_instrumentor.rb +186 -0
  41. data/lib/one_apm/agent/pipe/pipe_channel_manager.rb +275 -0
  42. data/lib/one_apm/agent/pipe/pipe_service.rb +81 -0
  43. data/lib/one_apm/agent/sampler.rb +55 -0
  44. data/lib/one_apm/agent/sampler_collection.rb +65 -0
  45. data/lib/one_apm/agent/samplers/cpu_sampler.rb +49 -0
  46. data/lib/one_apm/agent/samplers/delayed_job_sampler.rb +109 -0
  47. data/lib/one_apm/agent/samplers/memory_sampler.rb +144 -0
  48. data/lib/one_apm/agent/samplers/object_sampler.rb +22 -0
  49. data/lib/one_apm/agent/samplers/vm_sampler.rb +124 -0
  50. data/lib/one_apm/agent/synthetics_monitor.rb +48 -0
  51. data/lib/one_apm/agent/threading/agent_thread.rb +74 -0
  52. data/lib/one_apm/agent/threading/backtrace_node.rb +133 -0
  53. data/lib/one_apm/agent/threading/backtrace_service.rb +259 -0
  54. data/lib/one_apm/agent/threading/thread_profile.rb +155 -0
  55. data/lib/one_apm/collector/collector/helper.rb +139 -0
  56. data/lib/one_apm/collector/collector/http_connection.rb +254 -0
  57. data/lib/one_apm/collector/collector/server_methods.rb +71 -0
  58. data/lib/one_apm/collector/collector_service.rb +123 -0
  59. data/lib/one_apm/collector/commands/agent_command.rb +17 -0
  60. data/lib/one_apm/collector/commands/thread_profiler_session.rb +108 -0
  61. data/lib/one_apm/collector/commands/xray_session.rb +53 -0
  62. data/lib/one_apm/collector/commands/xray_session_collection.rb +156 -0
  63. data/lib/one_apm/collector/containers/agent_command_router.rb +153 -0
  64. data/lib/one_apm/collector/containers/custom_event_aggregator.rb +94 -0
  65. data/lib/one_apm/collector/containers/error_collector.rb +349 -0
  66. data/lib/one_apm/collector/containers/sql_sampler.rb +331 -0
  67. data/lib/one_apm/collector/containers/stats_engine.rb +34 -0
  68. data/lib/one_apm/collector/containers/transaction_event_aggregator.rb +249 -0
  69. data/lib/one_apm/collector/containers/transaction_sampler.rb +352 -0
  70. data/lib/one_apm/collector/containers/utilization_data.rb +36 -0
  71. data/lib/one_apm/collector/stats_engine/gc_profiler.rb +106 -0
  72. data/lib/one_apm/collector/stats_engine/metric_stats.rb +243 -0
  73. data/lib/one_apm/collector/stats_engine/stats_hash.rb +105 -0
  74. data/lib/one_apm/configuration.rb +429 -0
  75. data/lib/one_apm/configuration/autostart.rb +41 -0
  76. data/lib/one_apm/configuration/default_source.rb +1026 -0
  77. data/lib/one_apm/configuration/environment_source.rb +113 -0
  78. data/lib/one_apm/configuration/high_security_source.rb +56 -0
  79. data/lib/one_apm/configuration/manual_source.rb +13 -0
  80. data/lib/one_apm/configuration/server_source.rb +60 -0
  81. data/lib/one_apm/configuration/yaml_source.rb +134 -0
  82. data/lib/one_apm/errors/agent_errors.rb +26 -0
  83. data/lib/one_apm/errors/internal_agent_error.rb +16 -0
  84. data/lib/one_apm/errors/noticed_error.rb +79 -0
  85. data/lib/one_apm/frameworks/external.rb +15 -0
  86. data/lib/one_apm/frameworks/rails.rb +103 -0
  87. data/lib/one_apm/frameworks/rails3.rb +37 -0
  88. data/lib/one_apm/frameworks/rails4.rb +21 -0
  89. data/lib/one_apm/frameworks/ruby.rb +21 -0
  90. data/lib/one_apm/frameworks/sinatra.rb +12 -0
  91. data/lib/one_apm/inst/3rd/active_merchant.rb +35 -0
  92. data/lib/one_apm/inst/3rd/acts_as_solr.rb +70 -0
  93. data/lib/one_apm/inst/3rd/authlogic.rb +23 -0
  94. data/lib/one_apm/inst/3rd/sunspot.rb +31 -0
  95. data/lib/one_apm/inst/background_job/active_job.rb +88 -0
  96. data/lib/one_apm/inst/background_job/delayed_job.rb +52 -0
  97. data/lib/one_apm/inst/background_job/delayed_job_injection.rb +8 -0
  98. data/lib/one_apm/inst/background_job/resque.rb +107 -0
  99. data/lib/one_apm/inst/background_job/sidekiq.rb +64 -0
  100. data/lib/one_apm/inst/dispatcher/passenger.rb +25 -0
  101. data/lib/one_apm/inst/dispatcher/rainbows.rb +23 -0
  102. data/lib/one_apm/inst/framework/grape.rb +94 -0
  103. data/lib/one_apm/inst/framework/padrino.rb +30 -0
  104. data/lib/one_apm/inst/framework/sinatra.rb +185 -0
  105. data/lib/one_apm/inst/framework/sinatra/ignorer.rb +50 -0
  106. data/lib/one_apm/inst/framework/sinatra/transaction_namer.rb +54 -0
  107. data/lib/one_apm/inst/http_clients/curb.rb +189 -0
  108. data/lib/one_apm/inst/http_clients/excon.rb +70 -0
  109. data/lib/one_apm/inst/http_clients/excon/connection.rb +31 -0
  110. data/lib/one_apm/inst/http_clients/excon/middleware.rb +55 -0
  111. data/lib/one_apm/inst/http_clients/httpclient.rb +44 -0
  112. data/lib/one_apm/inst/http_clients/net.rb +34 -0
  113. data/lib/one_apm/inst/http_clients/typhoeus.rb +76 -0
  114. data/lib/one_apm/inst/nosql/memcache.rb +134 -0
  115. data/lib/one_apm/inst/nosql/mongo.rb +126 -0
  116. data/lib/one_apm/inst/nosql/mongo_moped.rb +85 -0
  117. data/lib/one_apm/inst/nosql/redis.rb +83 -0
  118. data/lib/one_apm/inst/orm/active_record.rb +99 -0
  119. data/lib/one_apm/inst/orm/active_record_4.rb +28 -0
  120. data/lib/one_apm/inst/orm/data_mapper.rb +180 -0
  121. data/lib/one_apm/inst/orm/sequel.rb +47 -0
  122. data/lib/one_apm/inst/rack.rb +38 -0
  123. data/lib/one_apm/inst/rack/rack.rb +44 -0
  124. data/lib/one_apm/inst/rack/rack_builder.rb +51 -0
  125. data/lib/one_apm/inst/rails/action_controller.rb +118 -0
  126. data/lib/one_apm/inst/rails/action_web_service.rb +44 -0
  127. data/lib/one_apm/inst/rails/errors.rb +43 -0
  128. data/lib/one_apm/inst/rails3/action_controller.rb +172 -0
  129. data/lib/one_apm/inst/rails3/errors.rb +43 -0
  130. data/lib/one_apm/inst/rails4/action_controller.rb +27 -0
  131. data/lib/one_apm/inst/rails4/action_controller_subscriber.rb +121 -0
  132. data/lib/one_apm/inst/rails4/action_view.rb +23 -0
  133. data/lib/one_apm/inst/rails4/action_view_subscriber.rb +93 -0
  134. data/lib/one_apm/inst/rails4/active_record_subscriber.rb +96 -0
  135. data/lib/one_apm/inst/rails4/errors.rb +42 -0
  136. data/lib/one_apm/inst/rails_middleware.rb +40 -0
  137. data/lib/one_apm/inst/support/evented_subscriber.rb +98 -0
  138. data/lib/one_apm/inst/support/ignore_actions.rb +39 -0
  139. data/lib/one_apm/inst/support/queue_time.rb +76 -0
  140. data/lib/one_apm/inst/transaction_base.rb +405 -0
  141. data/lib/one_apm/logger/agent_logger.rb +206 -0
  142. data/lib/one_apm/logger/audit_logger.rb +78 -0
  143. data/lib/one_apm/logger/memory_logger.rb +50 -0
  144. data/lib/one_apm/logger/null_logger.rb +19 -0
  145. data/lib/one_apm/metrics/metric_data.rb +72 -0
  146. data/lib/one_apm/metrics/metric_spec.rb +82 -0
  147. data/lib/one_apm/metrics/stats.rb +173 -0
  148. data/lib/one_apm/probe.rb +16 -0
  149. data/lib/one_apm/probe/framework_loader.rb +53 -0
  150. data/lib/one_apm/probe/instance_methods.rb +105 -0
  151. data/lib/one_apm/probe/instrumentation.rb +60 -0
  152. data/lib/one_apm/rack/browser_monitoring.rb +144 -0
  153. data/lib/one_apm/rack/middleware_base.rb +27 -0
  154. data/lib/one_apm/rack/middleware_hooks.rb +17 -0
  155. data/lib/one_apm/rack/middleware_tracing.rb +81 -0
  156. data/lib/one_apm/rack/middleware_wrapper.rb +86 -0
  157. data/lib/one_apm/support/chained_call.rb +15 -0
  158. data/lib/one_apm/support/coerce.rb +81 -0
  159. data/lib/one_apm/support/collection_helper.rb +79 -0
  160. data/lib/one_apm/support/dotted_hash.rb +45 -0
  161. data/lib/one_apm/support/encoders.rb +34 -0
  162. data/lib/one_apm/support/environment_report.rb +127 -0
  163. data/lib/one_apm/support/event_buffer.rb +82 -0
  164. data/lib/one_apm/support/event_buffer/sampled_buffer.rb +45 -0
  165. data/lib/one_apm/support/event_buffer/sized_buffer.rb +21 -0
  166. data/lib/one_apm/support/event_buffer/synthetics_event_buffer.rb +40 -0
  167. data/lib/one_apm/support/helper.rb +49 -0
  168. data/lib/one_apm/support/hostname.rb +13 -0
  169. data/lib/one_apm/support/http_clients/curb_wrappers.rb +65 -0
  170. data/lib/one_apm/support/http_clients/excon_wrappers.rb +63 -0
  171. data/lib/one_apm/support/http_clients/httpclient_wrappers.rb +61 -0
  172. data/lib/one_apm/support/http_clients/net_http_wrappers.rb +48 -0
  173. data/lib/one_apm/support/http_clients/typhoeus_wrappers.rb +73 -0
  174. data/lib/one_apm/support/http_clients/uri_util.rb +39 -0
  175. data/lib/one_apm/support/json_marshaller.rb +68 -0
  176. data/lib/one_apm/support/json_wrapper.rb +130 -0
  177. data/lib/one_apm/support/language_support.rb +142 -0
  178. data/lib/one_apm/support/library_detection.rb +119 -0
  179. data/lib/one_apm/support/local_environment.rb +196 -0
  180. data/lib/one_apm/support/marshaller.rb +62 -0
  181. data/lib/one_apm/support/method_tracer.rb +334 -0
  182. data/lib/one_apm/support/method_tracer/helpers.rb +92 -0
  183. data/lib/one_apm/support/method_tracer/traced_method_stack.rb +103 -0
  184. data/lib/one_apm/support/obfuscator.rb +47 -0
  185. data/lib/one_apm/support/okjson.rb +601 -0
  186. data/lib/one_apm/support/parameter_filtering.rb +35 -0
  187. data/lib/one_apm/support/rules_engine.rb +56 -0
  188. data/lib/one_apm/support/rules_engine/replacement_rule.rb +80 -0
  189. data/lib/one_apm/support/rules_engine/segment_terms_rule.rb +46 -0
  190. data/lib/one_apm/support/server.rb +11 -0
  191. data/lib/one_apm/support/supported_versions.rb +257 -0
  192. data/lib/one_apm/support/system_info.rb +211 -0
  193. data/lib/one_apm/support/timer_lib.rb +29 -0
  194. data/lib/one_apm/support/version_number.rb +51 -0
  195. data/lib/one_apm/support/vm.rb +30 -0
  196. data/lib/one_apm/support/vm/jruby_vm.rb +38 -0
  197. data/lib/one_apm/support/vm/monotonic_gc_profiler.rb +43 -0
  198. data/lib/one_apm/support/vm/mri_vm.rb +85 -0
  199. data/lib/one_apm/support/vm/rubinius_vm.rb +129 -0
  200. data/lib/one_apm/support/vm/snapshot.rb +18 -0
  201. data/lib/one_apm/transaction.rb +336 -0
  202. data/lib/one_apm/transaction/class_methods.rb +132 -0
  203. data/lib/one_apm/transaction/instance_helpers.rb +82 -0
  204. data/lib/one_apm/transaction/metric_constants.rb +42 -0
  205. data/lib/one_apm/transaction/sample_buffer/force_persist_sample_buffer.rb +21 -0
  206. data/lib/one_apm/transaction/sample_buffer/slowest_sample_buffer.rb +21 -0
  207. data/lib/one_apm/transaction/sample_buffer/synthetics_sample_buffer.rb +21 -0
  208. data/lib/one_apm/transaction/sample_buffer/transaction_sample_buffer.rb +101 -0
  209. data/lib/one_apm/transaction/sample_buffer/xray_sample_buffer.rb +60 -0
  210. data/lib/one_apm/transaction/segment.rb +193 -0
  211. data/lib/one_apm/transaction/segment_summary.rb +51 -0
  212. data/lib/one_apm/transaction/thread_local_access.rb +73 -0
  213. data/lib/one_apm/transaction/transaction_analysis.rb +78 -0
  214. data/lib/one_apm/transaction/transaction_apdex.rb +20 -0
  215. data/lib/one_apm/transaction/transaction_cpu.rb +22 -0
  216. data/lib/one_apm/transaction/transaction_finish_append.rb +67 -0
  217. data/lib/one_apm/transaction/transaction_ignore.rb +33 -0
  218. data/lib/one_apm/transaction/transaction_jruby_functions.rb +40 -0
  219. data/lib/one_apm/transaction/transaction_metrics.rb +53 -0
  220. data/lib/one_apm/transaction/transaction_name.rb +90 -0
  221. data/lib/one_apm/transaction/transaction_namer.rb +49 -0
  222. data/lib/one_apm/transaction/transaction_sample.rb +204 -0
  223. data/lib/one_apm/transaction/transaction_sample_builder.rb +168 -0
  224. data/lib/one_apm/transaction/transaction_state.rb +149 -0
  225. data/lib/one_apm/transaction/transaction_summary.rb +28 -0
  226. data/lib/one_apm/transaction/transaction_synthetics.rb +40 -0
  227. data/lib/one_apm/transaction/transaction_timings.rb +54 -0
  228. data/lib/one_apm/version.rb +13 -0
  229. data/lib/oneapm_rpm.rb +16 -0
  230. data/lib/sequel/extensions/oneapm_instrumentation.rb +84 -0
  231. data/lib/sequel/plugins/oneapm_instrumentation.rb +66 -0
  232. data/oneapm.yml +135 -0
  233. data/oneapm_rpm.gemspec +58 -0
  234. metadata +474 -0
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ require 'one_apm/agent/sampler'
4
+
5
+ module OneApm
6
+ module Agent
7
+ module Samplers
8
+ class ObjectSampler < OneApm::Agent::Sampler
9
+ named :object
10
+
11
+ def self.supported_on_this_platform?
12
+ OneApm::LanguageSupport.object_space_usable? && ObjectSpace.respond_to?(:live_objects)
13
+ end
14
+
15
+ def poll
16
+ live_objects = ObjectSpace.live_objects
17
+ OneApm::Agent.record_metric("GC/objects", live_objects)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,124 @@
1
+ # encoding: utf-8
2
+
3
+ require 'one_apm/agent/sampler'
4
+ require 'one_apm/support/vm'
5
+
6
+ module OneApm
7
+ module Agent
8
+ module Samplers
9
+ class VMSampler < Sampler
10
+ GC_RUNS_METRIC = 'RubyVM/GC/runs'.freeze
11
+ HEAP_LIVE_METRIC = 'RubyVM/GC/heap_live'.freeze
12
+ HEAP_FREE_METRIC = 'RubyVM/GC/heap_free'.freeze
13
+ THREAD_COUNT_METRIC = 'RubyVM/Threads/all'.freeze
14
+ OBJECT_ALLOCATIONS_METRIC = 'RubyVM/GC/total_allocated_object'.freeze
15
+ MAJOR_GC_METRIC = 'RubyVM/GC/major_gc_count'.freeze
16
+ MINOR_GC_METRIC = 'RubyVM/GC/minor_gc_count'.freeze
17
+ METHOD_INVALIDATIONS_METRIC = 'RubyVM/CacheInvalidations/method'.freeze
18
+ CONSTANT_INVALIDATIONS_METRIC = 'RubyVM/CacheInvalidations/constant'.freeze
19
+
20
+ attr_reader :transaction_count
21
+
22
+ named :vm
23
+
24
+ def initialize
25
+ @lock = Mutex.new
26
+ @transaction_count = 0
27
+ @last_snapshot = take_snapshot
28
+ end
29
+
30
+ def take_snapshot
31
+ OneApm::Support::VM.snapshot
32
+ end
33
+
34
+ def setup_events(event_listener)
35
+ event_listener.subscribe(:transaction_finished, &method(:on_transaction_finished))
36
+ end
37
+
38
+ def on_transaction_finished(*_)
39
+ @lock.synchronize { @transaction_count += 1 }
40
+ end
41
+
42
+ def reset_transaction_count
43
+ @lock.synchronize do
44
+ old_count = @transaction_count
45
+ @transaction_count = 0
46
+ old_count
47
+ end
48
+ end
49
+
50
+ def record_gc_runs_metric(snapshot, txn_count) #THREAD_LOCAL_ACCESS
51
+ if snapshot.gc_total_time || snapshot.gc_runs
52
+ if snapshot.gc_total_time
53
+ gc_time = snapshot.gc_total_time - @last_snapshot.gc_total_time.to_f
54
+ end
55
+ if snapshot.gc_runs
56
+ gc_runs = snapshot.gc_runs - @last_snapshot.gc_runs
57
+ end
58
+ wall_clock_time = snapshot.taken_at - @last_snapshot.taken_at
59
+ OneApm::Agent.instance.stats_engine.tl_record_unscoped_metrics(GC_RUNS_METRIC) do |stats|
60
+ stats.call_count += txn_count
61
+ stats.total_call_time += gc_runs if gc_runs
62
+ stats.total_exclusive_time += gc_time if gc_time
63
+ stats.max_call_time = (gc_time.nil? ? 0 : 1)
64
+ stats.sum_of_squares += wall_clock_time
65
+ end
66
+ end
67
+ end
68
+
69
+ def record_delta(snapshot, key, metric, txn_count) #THREAD_LOCAL_ACCESS
70
+ value = snapshot.send(key)
71
+ if value
72
+ delta = value - @last_snapshot.send(key)
73
+ OneApm::Agent.instance.stats_engine.tl_record_unscoped_metrics(metric) do |stats|
74
+ stats.call_count += txn_count
75
+ stats.total_call_time += delta
76
+ end
77
+ end
78
+ end
79
+
80
+ def record_gauge_metric(metric_name, value) #THREAD_LOCAL_ACCESS
81
+ OneApm::Agent.instance.stats_engine.tl_record_unscoped_metrics(metric_name) do |stats|
82
+ stats.call_count = value
83
+ stats.sum_of_squares = 1
84
+ end
85
+ end
86
+
87
+ def record_heap_live_metric(snapshot)
88
+ if snapshot.heap_live
89
+ record_gauge_metric(HEAP_LIVE_METRIC, snapshot.heap_live)
90
+ end
91
+ end
92
+
93
+ def record_heap_free_metric(snapshot)
94
+ if snapshot.heap_free
95
+ record_gauge_metric(HEAP_FREE_METRIC, snapshot.heap_free)
96
+ end
97
+ end
98
+
99
+ def record_thread_count_metric(snapshot)
100
+ if snapshot.thread_count
101
+ record_gauge_metric(THREAD_COUNT_METRIC, snapshot.thread_count)
102
+ end
103
+ end
104
+
105
+ def poll
106
+ snap = take_snapshot
107
+ tcount = reset_transaction_count
108
+
109
+ record_gc_runs_metric(snap, tcount)
110
+ record_delta(snap, :total_allocated_object, OBJECT_ALLOCATIONS_METRIC, tcount)
111
+ record_delta(snap, :major_gc_count, MAJOR_GC_METRIC, tcount)
112
+ record_delta(snap, :minor_gc_count, MINOR_GC_METRIC, tcount)
113
+ record_delta(snap, :method_cache_invalidations, METHOD_INVALIDATIONS_METRIC, tcount)
114
+ record_delta(snap, :constant_cache_invalidations, CONSTANT_INVALIDATIONS_METRIC, tcount)
115
+ record_heap_live_metric(snap)
116
+ record_heap_free_metric(snap)
117
+ record_thread_count_metric(snap)
118
+
119
+ @last_snapshot = snap
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'one_apm/agent/inbound_request_monitor'
4
+
5
+ module OneApm
6
+ module Agent
7
+ class SyntheticsMonitor < InboundRequestMonitor
8
+ SYNTHETICS_HEADER_KEY = 'HTTP_X_ONEAPM_SYNTHETICS'.freeze
9
+
10
+ SUPPORTED_VERSION = 1
11
+ EXPECTED_PAYLOAD_LENGTH = 5
12
+
13
+ def on_finished_configuring(events)
14
+ events.subscribe(:before_call, &method(:on_before_call))
15
+ end
16
+
17
+ def on_before_call(request) #THREAD_LOCAL_ACCESS
18
+ encoded_header = request[SYNTHETICS_HEADER_KEY]
19
+ return unless encoded_header
20
+
21
+ incoming_payload = deserialize_header(encoded_header, SYNTHETICS_HEADER_KEY)
22
+
23
+ return unless incoming_payload &&
24
+ is_valid_payload?(incoming_payload) &&
25
+ is_supported_version?(incoming_payload) &&
26
+ is_trusted?(incoming_payload)
27
+
28
+ state = OneApm::TransactionState.tl_get
29
+ txn = state.current_transaction
30
+ txn.raw_synthetics_header = encoded_header
31
+ txn.synthetics_payload = incoming_payload
32
+ end
33
+
34
+ def is_supported_version?(incoming_payload)
35
+ incoming_payload.first == SUPPORTED_VERSION
36
+ end
37
+
38
+ def is_trusted?(incoming_payload)
39
+ account_id = incoming_payload[1]
40
+ OneApm::Agent.config[:trusted_account_ids].include?(account_id)
41
+ end
42
+
43
+ def is_valid_payload?(incoming_payload)
44
+ incoming_payload.length == EXPECTED_PAYLOAD_LENGTH
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+
3
+ module OneApm
4
+ module Agent
5
+ module Threading
6
+ class AgentThread
7
+
8
+ def self.create(label, &blk)
9
+ ::OneApm::Agent.logger.debug("Creating OneApm thread: #{label}")
10
+ wrapped_blk = Proc.new do
11
+ begin
12
+ blk.call
13
+ rescue => e
14
+ ::OneApm::Agent.logger.error("Thread #{label} exited with error", e)
15
+ rescue Exception => e
16
+ ::OneApm::Agent.logger.error("Thread #{label} exited with exception. Re-raising in case of interupt.", e)
17
+ raise
18
+ ensure
19
+ ::OneApm::Agent.logger.debug("Exiting OneApm thread: #{label}")
20
+ end
21
+ end
22
+
23
+ thread = backing_thread_class.new(&wrapped_blk)
24
+ thread[:oneapm_label] = label
25
+ thread
26
+ end
27
+
28
+ # Simplifies testing if we don't directly use ::Thread.list, so keep
29
+ # the accessor for it here on AgentThread to use and stub.
30
+ def self.list
31
+ backing_thread_class.list
32
+ end
33
+
34
+ def self.bucket_thread(thread, profile_agent_code) #THREAD_LOCAL_ACCESS
35
+ if thread.key?(:oneapm_label)
36
+ profile_agent_code ? :agent : :ignore
37
+ else
38
+ state = TransactionState.tl_state_for(thread)
39
+ if state.in_background_transaction?
40
+ :background
41
+ elsif state.in_web_transaction?
42
+ :request
43
+ else
44
+ :other
45
+ end
46
+ end
47
+ end
48
+
49
+ def self.scrub_backtrace(thread, profile_agent_code)
50
+ begin
51
+ bt = thread.backtrace
52
+ rescue Exception => e
53
+ ::OneApm::Agent.logger.debug("Failed to backtrace #{thread.inspect}: #{e.class.name}: #{e.to_s}")
54
+ end
55
+ return nil unless bt
56
+ bt.reject! { |t| t.include?('one_apm') } unless profile_agent_code
57
+ bt
58
+ end
59
+
60
+ # To allow tests to swap out Thread for a synchronous alternative,
61
+ # surface the backing class we'll use from the class level.
62
+ @backing_thread_class = ::Thread
63
+
64
+ def self.backing_thread_class
65
+ @backing_thread_class
66
+ end
67
+
68
+ def self.backing_thread_class=(clazz)
69
+ @backing_thread_class = clazz
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,133 @@
1
+ # encoding: utf-8
2
+
3
+ module OneApm
4
+ module Agent
5
+ module Threading
6
+ MAX_THREAD_PROFILE_DEPTH = 500
7
+ UNKNOWN_LINE_NUMBER = -1
8
+
9
+ class BacktraceBase
10
+ attr_reader :children
11
+
12
+ def initialize
13
+ @children = []
14
+ @depth = 0
15
+ end
16
+
17
+ def add_child_unless_present(child)
18
+ child.depth = @depth + 1
19
+ @children << child unless @children.include? child
20
+ end
21
+
22
+ def add_child(child)
23
+ child.depth = @depth + 1
24
+ @children << child
25
+ end
26
+
27
+ def find_child(raw_line)
28
+ @children.find { |child| child.raw_line == raw_line }
29
+ end
30
+ end
31
+
32
+
33
+ class BacktraceRoot < BacktraceBase
34
+ attr_reader :flattened
35
+
36
+ def initialize
37
+ super
38
+ @flattened = []
39
+ end
40
+
41
+ def ==(other)
42
+ true # all roots are at the same depth and have no raw_line
43
+ end
44
+
45
+ def as_array
46
+ @children.map { |c| c.as_array }.compact
47
+ end
48
+
49
+ def aggregate(backtrace)
50
+ current = self
51
+
52
+ depth = 0
53
+ backtrace.reverse_each do |frame|
54
+ break if depth >= MAX_THREAD_PROFILE_DEPTH
55
+
56
+ existing_node = current.find_child(frame)
57
+ if existing_node
58
+ node = existing_node
59
+ else
60
+ node = Threading::BacktraceNode.new(frame)
61
+ current.add_child(node)
62
+ @flattened << node
63
+ end
64
+
65
+ node.runnable_count += 1
66
+ current = node
67
+ depth += 1
68
+ end
69
+ end
70
+
71
+ def dump_string
72
+ result = "#<BacktraceRoot:#{object_id}>"
73
+ child_results = @children.map { |c| c.dump_string(2) }.join("\n")
74
+ result << "\n" unless child_results.empty?
75
+ result << child_results
76
+ end
77
+ end
78
+
79
+
80
+ class BacktraceNode < BacktraceBase
81
+ attr_reader :file, :method, :line_no, :raw_line, :as_array
82
+ attr_accessor :runnable_count, :depth
83
+
84
+ def initialize(line)
85
+ super()
86
+ @raw_line = line
87
+ @children = []
88
+ @runnable_count = 0
89
+ end
90
+
91
+ def ==(other)
92
+ (
93
+ @raw_line == other.raw_line &&
94
+ @depth == other.depth &&
95
+ @runnable_count == other.runnable_count
96
+ )
97
+ end
98
+
99
+ def mark_for_array_conversion
100
+ @as_array = []
101
+ end
102
+
103
+ include OneApm::Coerce
104
+
105
+ def complete_array_conversion
106
+ child_arrays = @children.map { |c| c.as_array }.compact
107
+
108
+ file, method, line = parse_backtrace_frame(@raw_line)
109
+
110
+ @as_array << [string(file), string(method), line ? int(line) : UNKNOWN_LINE_NUMBER]
111
+ @as_array << int(@runnable_count)
112
+ @as_array << 0
113
+ @as_array << child_arrays
114
+ end
115
+
116
+ def dump_string(indent=0)
117
+ @file, @method, @line_no = parse_backtrace_frame(@raw_line)
118
+ result = "#{" " * indent}#<BacktraceNode:#{object_id} [#{@runnable_count}] #{@file}:#{@line_no} in #{@method}>"
119
+ child_results = @children.map { |c| c.dump_string(indent+2) }.join("\n")
120
+ result << "\n" unless child_results.empty?
121
+ result << child_results
122
+ end
123
+
124
+ # Returns [filename, method, line number]
125
+ def parse_backtrace_frame(frame)
126
+ frame =~ /([^:]*)(\:(\d+))?\:in `(.*)'/
127
+ [$1, $4, $3] # sic
128
+ end
129
+ end
130
+
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,259 @@
1
+ # encoding: utf-8
2
+
3
+ module OneApm
4
+ module Agent
5
+ module Threading
6
+ class BacktraceService
7
+ ALL_TRANSACTIONS = "**ALL**".freeze
8
+
9
+ def self.is_supported?
10
+ RUBY_VERSION >= "1.9.2"
11
+ end
12
+
13
+ attr_reader :worker_loop, :buffer,
14
+ :effective_polling_period,
15
+ :overhead_percent_threshold
16
+ attr_accessor :worker_thread, :profile_agent_code
17
+
18
+ def initialize(event_listener=nil)
19
+ @profiles = {}
20
+ @buffer = {}
21
+
22
+ # synchronizes access to @profiles and @buffer above
23
+ @lock = Mutex.new
24
+
25
+ @running = false
26
+ @profile_agent_code = false
27
+ @worker_loop = OneApm::Agent::WorkerLoop.new
28
+
29
+ # Memoize overhead % to avoid getting stale OR looked up every poll
30
+ @overhead_percent_threshold = OneApm::Agent.config[:'xray_session.max_profile_overhead']
31
+ OneApm::Agent.config.register_callback(:'xray_session.max_profile_overhead') do |new_value|
32
+ @overhead_percent_threshold = new_value
33
+ end
34
+
35
+ if event_listener
36
+ event_listener.subscribe(:transaction_finished, &method(:on_transaction_finished))
37
+ end
38
+ end
39
+
40
+ # Public interface
41
+
42
+ def running?
43
+ @running
44
+ end
45
+
46
+ def subscribe(transaction_name, command_arguments={})
47
+ if !self.class.is_supported?
48
+ OneApm::Agent.logger.debug("Backtracing not supported, so not subscribing transaction '#{transaction_name}'")
49
+ return
50
+ end
51
+
52
+ OneApm::Agent.logger.debug("Backtrace Service subscribing transaction '#{transaction_name}'")
53
+
54
+ profile = ThreadProfile.new(command_arguments)
55
+
56
+ @lock.synchronize do
57
+ @profiles[transaction_name] = profile
58
+ update_values_from_profiles
59
+ end
60
+
61
+ start
62
+ profile
63
+ end
64
+
65
+ def unsubscribe(transaction_name)
66
+ return unless self.class.is_supported?
67
+
68
+ OneApm::Agent.logger.debug("Backtrace Service unsubscribing transaction '#{transaction_name}'")
69
+ @lock.synchronize do
70
+ @profiles.delete(transaction_name)
71
+ if @profiles.empty?
72
+ stop
73
+ else
74
+ update_values_from_profiles
75
+ end
76
+ end
77
+ end
78
+
79
+ def update_values_from_profiles
80
+ self.effective_polling_period = find_effective_polling_period
81
+ self.profile_agent_code = should_profile_agent_code?
82
+ end
83
+
84
+ def subscribed?(transaction_name)
85
+ @lock.synchronize do
86
+ @profiles.has_key?(transaction_name)
87
+ end
88
+ end
89
+
90
+ def harvest(transaction_name)
91
+ @lock.synchronize do
92
+ if @profiles[transaction_name]
93
+ profile = @profiles.delete(transaction_name)
94
+ profile.finished_at = Time.now
95
+ @profiles[transaction_name] = ThreadProfile.new(profile.command_arguments)
96
+ profile
97
+ end
98
+ end
99
+ end
100
+
101
+ def on_transaction_finished(payload)
102
+ name = payload[:name]
103
+ start = payload[:start_timestamp]
104
+ duration = payload[:duration]
105
+ thread = payload[:thread] || Thread.current
106
+ @lock.synchronize do
107
+ backtraces = @buffer.delete(thread)
108
+ if backtraces && @profiles.has_key?(name)
109
+ aggregate_backtraces(backtraces, name, start, duration, thread)
110
+ end
111
+ end
112
+ end
113
+
114
+ # Internals
115
+
116
+ # This method is expected to be called with @lock held.
117
+ def aggregate_backtraces(backtraces, name, start, duration, thread)
118
+ end_time = start + duration
119
+ backtraces.each do |(timestamp, backtrace)|
120
+ if timestamp >= start && timestamp < end_time
121
+ @profiles[name].aggregate(backtrace, :request, thread)
122
+ end
123
+ end
124
+ end
125
+
126
+ def start
127
+ return if @running || !self.class.is_supported?
128
+
129
+ @running = true
130
+ self.worker_thread = AgentThread.create('Backtrace Service') do
131
+ # Not passing period because we expect it's already been set.
132
+ self.worker_loop.run(&method(:poll))
133
+ end
134
+ end
135
+
136
+ # This method is expected to be called with @lock held
137
+ def stop
138
+ return unless @running
139
+ @running = false
140
+ self.worker_loop.stop
141
+
142
+ @buffer = {}
143
+ end
144
+
145
+ def effective_polling_period=(new_period)
146
+ @effective_polling_period = new_period
147
+ self.worker_loop.period = new_period
148
+ end
149
+
150
+ def poll
151
+ poll_start = Time.now
152
+
153
+ @lock.synchronize do
154
+ AgentThread.list.each do |thread|
155
+ sample_thread(thread)
156
+ end
157
+ @profiles.each_value { |p| p.increment_poll_count }
158
+ @buffer.delete_if { |thread, _| !thread.alive? }
159
+ end
160
+
161
+ end_time = Time.now
162
+ adjust_polling_time(end_time, poll_start)
163
+ record_supportability_metrics(end_time, poll_start)
164
+ end
165
+
166
+ # This method is expected to be called with @lock held.
167
+ attr_reader :profiles
168
+
169
+ # This method is expected to be called with @lock held.
170
+ def should_buffer?(bucket)
171
+ bucket == :request && @profiles.keys.any? { |k| k != ALL_TRANSACTIONS }
172
+ end
173
+
174
+ # This method is expected to be called with @lock held.
175
+ def need_backtrace?(bucket)
176
+ (
177
+ bucket != :ignore &&
178
+ (@profiles[ALL_TRANSACTIONS] || should_buffer?(bucket))
179
+ )
180
+ end
181
+
182
+ MAX_BUFFER_LENGTH = 500
183
+
184
+ # This method is expected to be called with @lock held.
185
+ def buffer_backtrace_for_thread(thread, timestamp, backtrace, bucket)
186
+ if should_buffer?(bucket)
187
+ @buffer[thread] ||= []
188
+ if @buffer[thread].length < MAX_BUFFER_LENGTH
189
+ @buffer[thread] << [timestamp, backtrace]
190
+ else
191
+ OneApm::Agent.increment_metric('Supportability/XraySessions/DroppedBacktraces')
192
+ end
193
+ end
194
+ end
195
+
196
+ # This method is expected to be called with @lock held.
197
+ def aggregate_global_backtrace(backtrace, bucket, thread)
198
+ if @profiles[ALL_TRANSACTIONS]
199
+ @profiles[ALL_TRANSACTIONS].aggregate(backtrace, bucket, thread)
200
+ end
201
+ end
202
+
203
+ # This method is expected to be called with @lock held.
204
+ def sample_thread(thread)
205
+ bucket = AgentThread.bucket_thread(thread, @profile_agent_code)
206
+
207
+ if need_backtrace?(bucket)
208
+ timestamp = Time.now.to_f
209
+ backtrace = AgentThread.scrub_backtrace(thread, @profile_agent_code)
210
+ aggregate_global_backtrace(backtrace, bucket, thread)
211
+ buffer_backtrace_for_thread(thread, timestamp, backtrace, bucket)
212
+ end
213
+ end
214
+
215
+ # This method is expected to be called with @lock held.
216
+ def find_effective_polling_period
217
+ @profiles.values.map { |p| p.requested_period }.min
218
+ end
219
+
220
+ # This method is expected to be called with @lock held.
221
+ def should_profile_agent_code?
222
+ @profiles.values.any? { |p| p.profile_agent_code }
223
+ end
224
+
225
+ # If our overhead % exceeds the threshold, bump the next poll period
226
+ # relative to how much larger our overhead is than allowed
227
+ def adjust_polling_time(now, poll_start)
228
+ duration = now - poll_start
229
+ overhead_percent = duration / effective_polling_period
230
+
231
+ if overhead_percent > self.overhead_percent_threshold
232
+ scale_up_by = overhead_percent / self.overhead_percent_threshold
233
+ worker_loop.period = effective_polling_period * scale_up_by
234
+ else
235
+ worker_loop.period = effective_polling_period
236
+ end
237
+ end
238
+
239
+ def record_supportability_metrics(now, poll_start)
240
+ record_polling_time(now, poll_start)
241
+ record_skew(poll_start)
242
+ end
243
+
244
+ def record_polling_time(now, poll_start)
245
+ OneApm::Agent.record_metric('Supportability/ThreadProfiler/PollingTime', now - poll_start)
246
+ end
247
+
248
+ def record_skew(poll_start)
249
+ if @last_poll
250
+ skew = poll_start - @last_poll - worker_loop.period
251
+ OneApm::Agent.record_metric('Supportability/ThreadProfiler/Skew', skew)
252
+ end
253
+ @last_poll = poll_start
254
+ end
255
+
256
+ end
257
+ end
258
+ end
259
+ end