scout_apm 2.6.10 → 3.0.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (233) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -2
  3. data/.rubocop.yml +3 -11
  4. data/CHANGELOG.markdown +4 -362
  5. data/Gemfile +1 -14
  6. data/README.markdown +7 -52
  7. data/Rakefile +1 -0
  8. data/ext/allocations/allocations.c +1 -7
  9. data/ext/allocations/extconf.rb +0 -1
  10. data/ext/rusage/rusage.c +0 -26
  11. data/ext/stacks/extconf.rb +37 -0
  12. data/ext/stacks/scout_atomics.h +86 -0
  13. data/ext/stacks/stacks.c +811 -0
  14. data/lib/scout_apm/agent/logging.rb +69 -0
  15. data/lib/scout_apm/agent/reporting.rb +126 -0
  16. data/lib/scout_apm/agent.rb +259 -138
  17. data/lib/scout_apm/app_server_load.rb +15 -41
  18. data/lib/scout_apm/attribute_arranger.rb +3 -14
  19. data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -70
  20. data/lib/scout_apm/background_job_integrations/sidekiq.rb +24 -31
  21. data/lib/scout_apm/background_worker.rb +12 -23
  22. data/lib/scout_apm/capacity.rb +57 -0
  23. data/lib/scout_apm/config.rb +37 -206
  24. data/lib/scout_apm/context.rb +4 -20
  25. data/lib/scout_apm/deploy_integrations/capistrano_2.cap +12 -0
  26. data/lib/scout_apm/deploy_integrations/capistrano_2.rb +83 -0
  27. data/lib/scout_apm/deploy_integrations/capistrano_3.cap +12 -0
  28. data/lib/scout_apm/deploy_integrations/capistrano_3.rb +88 -0
  29. data/lib/scout_apm/environment.rb +28 -42
  30. data/lib/scout_apm/fake_store.rb +0 -12
  31. data/lib/scout_apm/framework_integrations/rails_2.rb +1 -2
  32. data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +6 -17
  33. data/lib/scout_apm/framework_integrations/sinatra.rb +1 -1
  34. data/lib/scout_apm/histogram.rb +3 -12
  35. data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
  36. data/lib/scout_apm/instant/middleware.rb +54 -202
  37. data/lib/scout_apm/instant_reporting.rb +7 -7
  38. data/lib/scout_apm/instruments/.DS_Store +0 -0
  39. data/lib/scout_apm/instruments/action_controller_rails_2.rb +9 -15
  40. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +76 -124
  41. data/lib/scout_apm/instruments/active_record.rb +29 -324
  42. data/lib/scout_apm/instruments/delayed_job.rb +57 -0
  43. data/lib/scout_apm/instruments/elasticsearch.rb +6 -10
  44. data/lib/scout_apm/instruments/grape.rb +9 -12
  45. data/lib/scout_apm/instruments/http_client.rb +7 -14
  46. data/lib/scout_apm/instruments/influxdb.rb +6 -10
  47. data/lib/scout_apm/instruments/middleware_detailed.rb +11 -15
  48. data/lib/scout_apm/instruments/middleware_summary.rb +5 -11
  49. data/lib/scout_apm/instruments/mongoid.rb +8 -39
  50. data/lib/scout_apm/instruments/moped.rb +6 -11
  51. data/lib/scout_apm/instruments/net_http.rb +9 -27
  52. data/lib/scout_apm/instruments/percentile_sampler.rb +23 -42
  53. data/lib/scout_apm/instruments/process/process_cpu.rb +6 -11
  54. data/lib/scout_apm/instruments/process/process_memory.rb +12 -17
  55. data/lib/scout_apm/instruments/rails_router.rb +6 -12
  56. data/lib/scout_apm/instruments/redis.rb +6 -10
  57. data/lib/scout_apm/instruments/sinatra.rb +4 -5
  58. data/lib/scout_apm/job_record.rb +2 -4
  59. data/lib/scout_apm/layaway.rb +34 -88
  60. data/lib/scout_apm/layaway_file.rb +3 -13
  61. data/lib/scout_apm/layer.rb +60 -25
  62. data/lib/scout_apm/layer_converters/allocation_metric_converter.rb +6 -7
  63. data/lib/scout_apm/layer_converters/converter_base.rb +14 -203
  64. data/lib/scout_apm/layer_converters/depth_first_walker.rb +10 -22
  65. data/lib/scout_apm/layer_converters/error_converter.rb +8 -8
  66. data/lib/scout_apm/layer_converters/job_converter.rb +50 -37
  67. data/lib/scout_apm/layer_converters/metric_converter.rb +19 -18
  68. data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +13 -13
  69. data/lib/scout_apm/layer_converters/slow_job_converter.rb +116 -52
  70. data/lib/scout_apm/layer_converters/slow_request_converter.rb +120 -51
  71. data/lib/scout_apm/metric_meta.rb +5 -0
  72. data/lib/scout_apm/metric_set.rb +1 -9
  73. data/lib/scout_apm/metric_stats.rb +8 -7
  74. data/lib/scout_apm/middleware.rb +9 -7
  75. data/lib/scout_apm/reporter.rb +24 -71
  76. data/lib/scout_apm/request_histograms.rb +0 -12
  77. data/lib/scout_apm/request_manager.rb +7 -5
  78. data/lib/scout_apm/scored_item_set.rb +0 -7
  79. data/lib/scout_apm/serializers/app_server_load_serializer.rb +0 -4
  80. data/lib/scout_apm/serializers/deploy_serializer.rb +16 -0
  81. data/lib/scout_apm/serializers/directive_serializer.rb +0 -4
  82. data/lib/scout_apm/serializers/payload_serializer.rb +4 -11
  83. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +16 -35
  84. data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +1 -2
  85. data/lib/scout_apm/server_integrations/passenger.rb +1 -1
  86. data/lib/scout_apm/server_integrations/puma.rb +2 -5
  87. data/lib/scout_apm/slow_job_policy.rb +13 -25
  88. data/lib/scout_apm/slow_job_record.rb +4 -13
  89. data/lib/scout_apm/slow_request_policy.rb +13 -25
  90. data/lib/scout_apm/slow_transaction.rb +5 -25
  91. data/lib/scout_apm/store.rb +32 -99
  92. data/lib/scout_apm/trace_compactor.rb +312 -0
  93. data/lib/scout_apm/tracer.rb +31 -35
  94. data/lib/scout_apm/tracked_request.rb +95 -262
  95. data/lib/scout_apm/utils/active_record_metric_name.rb +13 -88
  96. data/lib/scout_apm/utils/backtrace_parser.rb +4 -7
  97. data/lib/scout_apm/utils/fake_stacks.rb +87 -0
  98. data/lib/scout_apm/utils/installed_gems.rb +3 -7
  99. data/lib/scout_apm/utils/klass_helper.rb +2 -8
  100. data/lib/scout_apm/utils/null_logger.rb +13 -0
  101. data/lib/scout_apm/utils/sql_sanitizer.rb +5 -16
  102. data/lib/scout_apm/utils/sql_sanitizer_regex.rb +0 -7
  103. data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +0 -6
  104. data/lib/scout_apm/utils/unique_id.rb +0 -27
  105. data/lib/scout_apm/version.rb +2 -1
  106. data/lib/scout_apm.rb +25 -84
  107. data/scout_apm.gemspec +3 -17
  108. data/test/test_helper.rb +3 -57
  109. data/test/unit/agent_test.rb +54 -1
  110. data/test/unit/background_job_integrations/sidekiq_test.rb +3 -0
  111. data/test/unit/config_test.rb +12 -25
  112. data/test/unit/context_test.rb +4 -4
  113. data/test/unit/histogram_test.rb +4 -25
  114. data/test/unit/ignored_uris_test.rb +1 -1
  115. data/test/unit/instruments/active_record_instruments_test.rb +5 -0
  116. data/test/unit/layaway_test.rb +2 -62
  117. data/test/unit/serializers/payload_serializer_test.rb +15 -43
  118. data/test/unit/slow_request_policy_test.rb +6 -15
  119. data/test/unit/sql_sanitizer_test.rb +6 -53
  120. data/test/unit/store_test.rb +4 -73
  121. data/test/unit/utils/active_record_metric_name_test.rb +5 -59
  122. data/test/unit/utils/backtrace_parser_test.rb +1 -6
  123. data/tester.rb +53 -0
  124. metadata +28 -229
  125. data/.travis.yml +0 -26
  126. data/Guardfile +0 -43
  127. data/gems/README.md +0 -28
  128. data/gems/octoshark.gemfile +0 -4
  129. data/gems/rails3.gemfile +0 -5
  130. data/gems/rails4.gemfile +0 -4
  131. data/gems/rails5.gemfile +0 -4
  132. data/gems/rails6.gemfile +0 -4
  133. data/lib/scout_apm/agent/exit_handler.rb +0 -65
  134. data/lib/scout_apm/agent/preconditions.rb +0 -81
  135. data/lib/scout_apm/agent_context.rb +0 -261
  136. data/lib/scout_apm/auto_instrument/instruction_sequence.rb +0 -31
  137. data/lib/scout_apm/auto_instrument/layer.rb +0 -23
  138. data/lib/scout_apm/auto_instrument/parser.rb +0 -27
  139. data/lib/scout_apm/auto_instrument/rails.rb +0 -175
  140. data/lib/scout_apm/auto_instrument.rb +0 -5
  141. data/lib/scout_apm/background_job_integrations/legacy_sneakers.rb +0 -55
  142. data/lib/scout_apm/background_job_integrations/que.rb +0 -134
  143. data/lib/scout_apm/background_job_integrations/resque.rb +0 -88
  144. data/lib/scout_apm/background_job_integrations/shoryuken.rb +0 -124
  145. data/lib/scout_apm/background_job_integrations/sneakers.rb +0 -87
  146. data/lib/scout_apm/background_recorder.rb +0 -48
  147. data/lib/scout_apm/db_query_metric_set.rb +0 -97
  148. data/lib/scout_apm/db_query_metric_stats.rb +0 -102
  149. data/lib/scout_apm/debug.rb +0 -37
  150. data/lib/scout_apm/detailed_trace.rb +0 -217
  151. data/lib/scout_apm/error.rb +0 -27
  152. data/lib/scout_apm/error_service/error_buffer.rb +0 -39
  153. data/lib/scout_apm/error_service/error_record.rb +0 -211
  154. data/lib/scout_apm/error_service/ignored_exceptions.rb +0 -66
  155. data/lib/scout_apm/error_service/middleware.rb +0 -32
  156. data/lib/scout_apm/error_service/notifier.rb +0 -33
  157. data/lib/scout_apm/error_service/payload.rb +0 -47
  158. data/lib/scout_apm/error_service/periodic_work.rb +0 -17
  159. data/lib/scout_apm/error_service/railtie.rb +0 -11
  160. data/lib/scout_apm/error_service/sidekiq.rb +0 -80
  161. data/lib/scout_apm/error_service.rb +0 -32
  162. data/lib/scout_apm/extensions/config.rb +0 -87
  163. data/lib/scout_apm/extensions/transaction_callback_payload.rb +0 -74
  164. data/lib/scout_apm/git_revision.rb +0 -59
  165. data/lib/scout_apm/instrument_manager.rb +0 -88
  166. data/lib/scout_apm/instruments/action_view.rb +0 -141
  167. data/lib/scout_apm/instruments/http.rb +0 -48
  168. data/lib/scout_apm/instruments/memcached.rb +0 -43
  169. data/lib/scout_apm/instruments/resque.rb +0 -39
  170. data/lib/scout_apm/instruments/samplers.rb +0 -11
  171. data/lib/scout_apm/layer_children_set.rb +0 -86
  172. data/lib/scout_apm/layer_converters/database_converter.rb +0 -70
  173. data/lib/scout_apm/layer_converters/find_layer_by_type.rb +0 -38
  174. data/lib/scout_apm/layer_converters/histograms.rb +0 -15
  175. data/lib/scout_apm/layer_converters/trace_converter.rb +0 -184
  176. data/lib/scout_apm/limited_layer.rb +0 -126
  177. data/lib/scout_apm/logger.rb +0 -158
  178. data/lib/scout_apm/periodic_work.rb +0 -47
  179. data/lib/scout_apm/rack.rb +0 -26
  180. data/lib/scout_apm/remote/message.rb +0 -27
  181. data/lib/scout_apm/remote/recorder.rb +0 -57
  182. data/lib/scout_apm/remote/router.rb +0 -49
  183. data/lib/scout_apm/remote/server.rb +0 -60
  184. data/lib/scout_apm/reporting.rb +0 -143
  185. data/lib/scout_apm/serializers/db_query_serializer_to_json.rb +0 -15
  186. data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +0 -21
  187. data/lib/scout_apm/synchronous_recorder.rb +0 -30
  188. data/lib/scout_apm/tasks/doctor.rb +0 -75
  189. data/lib/scout_apm/tasks/support.rb +0 -22
  190. data/lib/scout_apm/transaction.rb +0 -13
  191. data/lib/scout_apm/transaction_time_consumed.rb +0 -51
  192. data/lib/scout_apm/utils/gzip_helper.rb +0 -24
  193. data/lib/scout_apm/utils/marshal_logging.rb +0 -90
  194. data/lib/scout_apm/utils/numbers.rb +0 -14
  195. data/lib/scout_apm/utils/scm.rb +0 -14
  196. data/lib/tasks/doctor.rake +0 -11
  197. data/test/tmp/README.md +0 -17
  198. data/test/unit/agent_context_test.rb +0 -15
  199. data/test/unit/auto_instrument/assignments-instrumented.rb +0 -31
  200. data/test/unit/auto_instrument/assignments.rb +0 -31
  201. data/test/unit/auto_instrument/controller-ast.txt +0 -57
  202. data/test/unit/auto_instrument/controller-instrumented.rb +0 -49
  203. data/test/unit/auto_instrument/controller.rb +0 -49
  204. data/test/unit/auto_instrument/rescue_from-instrumented.rb +0 -13
  205. data/test/unit/auto_instrument/rescue_from.rb +0 -13
  206. data/test/unit/auto_instrument_test.rb +0 -54
  207. data/test/unit/db_query_metric_set_test.rb +0 -67
  208. data/test/unit/db_query_metric_stats_test.rb +0 -113
  209. data/test/unit/error_service/error_buffer_test.rb +0 -25
  210. data/test/unit/error_service/ignored_exceptions_test.rb +0 -49
  211. data/test/unit/extensions/periodic_callbacks_test.rb +0 -58
  212. data/test/unit/extensions/transaction_callbacks_test.rb +0 -58
  213. data/test/unit/fake_store_test.rb +0 -10
  214. data/test/unit/git_revision_test.rb +0 -15
  215. data/test/unit/instruments/active_record_test.rb +0 -40
  216. data/test/unit/instruments/net_http_test.rb +0 -27
  217. data/test/unit/instruments/percentile_sampler_test.rb +0 -133
  218. data/test/unit/layer_children_set_test.rb +0 -97
  219. data/test/unit/layer_converters/depth_first_walker_test.rb +0 -70
  220. data/test/unit/layer_converters/metric_converter_test.rb +0 -22
  221. data/test/unit/layer_converters/stubs.rb +0 -33
  222. data/test/unit/limited_layer_test.rb +0 -53
  223. data/test/unit/logger_test.rb +0 -69
  224. data/test/unit/remote/test_message.rb +0 -13
  225. data/test/unit/remote/test_router.rb +0 -33
  226. data/test/unit/remote/test_server.rb +0 -15
  227. data/test/unit/request_histograms_test.rb +0 -17
  228. data/test/unit/tracer_test.rb +0 -76
  229. data/test/unit/tracked_request_test.rb +0 -71
  230. data/test/unit/transaction_test.rb +0 -14
  231. data/test/unit/transaction_time_consumed_test.rb +0 -46
  232. data/test/unit/utils/numbers_test.rb +0 -15
  233. data/test/unit/utils/scm.rb +0 -17
@@ -0,0 +1,811 @@
1
+ /*
2
+ * General idioms:
3
+ * - rb_* functions are attached to Ruby-accessible method calls (See Init_stacks)
4
+ * General approach:
5
+ * - Because of how rb_profile_frames works, it must be called from within
6
+ * each thread running, rather than from a 3rd party thread.
7
+ * - We setup a global timer tick. The handler simply sends a thread signal
8
+ * to each registered thread, which causes each thread to capture its own
9
+ * trace
10
+ */
11
+
12
+ /*
13
+ * Ruby lib
14
+ */
15
+ #include <ruby/ruby.h>
16
+ #include <ruby/debug.h>
17
+ #include <ruby/st.h>
18
+ #include <ruby/io.h>
19
+ #include <ruby/intern.h>
20
+
21
+ /*
22
+ * Std lib
23
+ */
24
+ #include <errno.h>
25
+ #include <inttypes.h>
26
+ #include <pthread.h>
27
+ #include <signal.h>
28
+ #include <stdbool.h>
29
+
30
+ /*
31
+ * System
32
+ */
33
+ #ifdef __linux__
34
+ #include <sys/syscall.h>
35
+ #endif
36
+ #include <sys/time.h>
37
+
38
+ #include "scout_atomics.h"
39
+
40
+
41
+ int scout_profiling_installed = 0;
42
+ int scout_profiling_running = 0;
43
+
44
+ ID sym_ScoutApm;
45
+ ID sym_Stacks;
46
+ ID sym_collect;
47
+ ID sym_scrub_bang;
48
+ VALUE ScoutApm;
49
+ VALUE Stacks;
50
+
51
+ VALUE mScoutApm;
52
+ VALUE mInstruments;
53
+ VALUE cStacks;
54
+
55
+ VALUE interval;
56
+
57
+ #define BUF_SIZE 512
58
+ #define MAX_TRACES 2000
59
+
60
+ #ifdef __linux__
61
+ #define NANO_SECOND_MULTIPLIER 1000000 // 1 millisecond = 1,000,000 Nanoseconds
62
+ const long INTERVAL = 1 * NANO_SECOND_MULTIPLIER; // milliseconds * NANO_SECOND_MULTIPLIER
63
+ // For support of thread id in timer_create
64
+ #define sigev_notify_thread_id _sigev_un._tid
65
+
66
+ #else // __linux__
67
+
68
+ const long INTERVAL = 1000; // 1ms
69
+
70
+ #endif
71
+
72
+ #ifdef T_IMEMO
73
+ #define VALID_RUBY_FRAME T_IMEMO
74
+ #else
75
+ #define VALID_RUBY_FRAME T_DATA
76
+ #endif
77
+
78
+
79
+
80
+ #ifdef RUBY_INTERNAL_EVENT_NEWOBJ
81
+
82
+ // Forward Declarations
83
+ static void init_thread_vars();
84
+ static void scout_profile_broadcast_signal_handler(int sig);
85
+ void scout_record_sample();
86
+ static void scout_start_thread_timer();
87
+ static void scout_stop_thread_timer();
88
+
89
+ ////////////////////////////////////////////////////////////////////////////////////////
90
+ // Per-Thread variables
91
+ ////////////////////////////////////////////////////////////////////////////////////////
92
+
93
+ struct c_trace {
94
+ int num_tracelines;
95
+ int lines_buf[BUF_SIZE];
96
+ VALUE frames_buf[BUF_SIZE];
97
+ };
98
+
99
+ static __thread struct c_trace *_traces;
100
+
101
+ static __thread atomic_bool_t _thread_registered = ATOMIC_INIT(false);
102
+ static __thread atomic_bool_t _ok_to_sample = ATOMIC_INIT(false);
103
+ static __thread atomic_bool_t _in_signal_handler = ATOMIC_INIT(false);
104
+
105
+ static __thread atomic_uint16_t _start_frame_index = ATOMIC_INIT(0);
106
+ static __thread atomic_uint16_t _start_trace_index = ATOMIC_INIT(0);
107
+ static __thread atomic_uint16_t _cur_traces_num = ATOMIC_INIT(0);
108
+
109
+ static __thread atomic_uint32_t _skipped_in_gc = ATOMIC_INIT(0);
110
+ static __thread atomic_uint32_t _skipped_in_signal_handler = ATOMIC_INIT(0);
111
+ static __thread atomic_uint32_t _skipped_in_job_registered = ATOMIC_INIT(0);
112
+ static __thread atomic_uint32_t _skipped_in_not_running = ATOMIC_INIT(0);
113
+
114
+ static __thread VALUE _gc_hook;
115
+ static __thread VALUE _ruby_thread;
116
+
117
+ static __thread atomic_bool_t _job_registered = ATOMIC_INIT(false);
118
+
119
+ #ifdef __linux__
120
+ static __thread timer_t _timerid;
121
+ static __thread struct sigevent _sev;
122
+ #endif
123
+
124
+ ////////////////////////////////////////////////////////////////////////////////////////
125
+ // Global variables
126
+ ////////////////////////////////////////////////////////////////////////////////////////
127
+
128
+ static int
129
+ scout_add_profiled_thread(pthread_t th)
130
+ {
131
+ if (ATOMIC_LOAD(&_thread_registered) == true) return 1;
132
+
133
+ init_thread_vars();
134
+ ATOMIC_STORE_BOOL(&_thread_registered, true);
135
+
136
+ return 1;
137
+ }
138
+
139
+ /*
140
+ * rb_scout_add_profiled_thread: adds the currently running thread to the head of the linked list
141
+ *
142
+ * Initializes thread locals:
143
+ * - ok_to_sample to false
144
+ * - start_frame_index and start_trace_index to 0
145
+ * - cur_traces_num to 0
146
+ */
147
+ static VALUE
148
+ rb_scout_add_profiled_thread(VALUE self)
149
+ {
150
+ scout_add_profiled_thread(pthread_self());
151
+ return Qtrue;
152
+ }
153
+
154
+ /*
155
+ * remove_profiled_thread: removes a thread from the linked list.
156
+ * if the linked list is empty, this is a noop
157
+ */
158
+ static int
159
+ remove_profiled_thread(pthread_t th)
160
+ {
161
+ if (ATOMIC_LOAD(&_thread_registered) == false) return 1;
162
+
163
+ ATOMIC_STORE_BOOL(&_ok_to_sample, false);
164
+
165
+ // Unregister the _gc_hook from Ruby ObjectSpace, then free it as well as the _traces struct it wrapped.
166
+ rb_gc_unregister_address(&_gc_hook);
167
+ xfree(&_gc_hook);
168
+ xfree(&_traces);
169
+
170
+ #ifdef __linux__
171
+ timer_delete(_timerid);
172
+ #endif
173
+
174
+ ATOMIC_STORE_BOOL(&_thread_registered, false);
175
+ return 0;
176
+ }
177
+
178
+ /* rb_scout_remove_profiled_thread: removes a thread from the linked list
179
+ *
180
+ */
181
+ static VALUE rb_scout_remove_profiled_thread(VALUE self)
182
+ {
183
+ remove_profiled_thread(pthread_self());
184
+ return Qtrue;
185
+ }
186
+
187
+
188
+ /* rb_scout_start_profiling: Installs the global timer
189
+ */
190
+ static VALUE
191
+ rb_scout_start_profiling(VALUE self)
192
+ {
193
+ if (scout_profiling_running) {
194
+ return Qtrue;
195
+ }
196
+
197
+ scout_profiling_running = 1;
198
+
199
+ return Qtrue;
200
+ }
201
+
202
+ /* rb_scout_uninstall_profiling: called when ruby is shutting down.
203
+ * NOTE: If ever this method should be called where Ruby should continue to run, we need to free our
204
+ * memory allocated in each profiled thread.
205
+ */
206
+ static VALUE
207
+ rb_scout_uninstall_profiling(VALUE self)
208
+ {
209
+ return Qnil;
210
+ }
211
+
212
+ static VALUE
213
+ rb_scout_install_profiling(VALUE self)
214
+ {
215
+ #ifndef __linux__
216
+ struct itimerval timer;
217
+ #endif
218
+ struct sigaction new_vtaction, old_vtaction;
219
+
220
+ // We can only install once. If uninstall is called, we will NOT be able to call install again.
221
+ // Instead, stop/start should be used to temporarily disable all ScoutProf sampling.
222
+ if (scout_profiling_installed) {
223
+ return Qfalse;
224
+ }
225
+
226
+ // Also set up an interrupt handler for when we broadcast an alarm
227
+ new_vtaction.sa_handler = scout_profile_broadcast_signal_handler;
228
+ new_vtaction.sa_flags = SA_RESTART;
229
+ sigemptyset(&new_vtaction.sa_mask);
230
+ sigaction(SIGALRM, &new_vtaction, &old_vtaction);
231
+
232
+ #ifndef __linux__
233
+ timer.it_interval.tv_sec = 0;
234
+ timer.it_interval.tv_usec = INTERVAL; //FIX2INT(interval);
235
+ timer.it_value = timer.it_interval;
236
+ setitimer(ITIMER_REAL, &timer, 0);
237
+ #endif
238
+
239
+ rb_define_const(cStacks, "INSTALLED", Qtrue);
240
+ scout_profiling_installed = 1;
241
+
242
+ return Qtrue;
243
+ }
244
+
245
+ ////////////////////////////////////////////////////////////////////////////////////////
246
+ // Per-Thread Handler
247
+ ////////////////////////////////////////////////////////////////////////////////////////
248
+
249
+ static void
250
+ scoutprof_gc_mark(void *data)
251
+ {
252
+ uint_fast16_t i;
253
+ int n;
254
+ for (i = 0; i < ATOMIC_LOAD(&_cur_traces_num); i++) {
255
+ for (n = 0; n < _traces[i].num_tracelines; n++) {
256
+ rb_gc_mark(_traces[i].frames_buf[n]);
257
+ }
258
+ }
259
+ }
260
+
261
+ static void
262
+ scout_parent_atfork_prepare()
263
+ {
264
+ // TODO: Should we track how much time the fork took?
265
+ if (ATOMIC_LOAD(&_ok_to_sample) == true) {
266
+ scout_stop_thread_timer();
267
+ }
268
+ }
269
+
270
+ static void
271
+ scout_parent_atfork_finish()
272
+ {
273
+ if (ATOMIC_LOAD(&_ok_to_sample) == true) {
274
+ scout_start_thread_timer();
275
+ }
276
+ }
277
+
278
+ static void
279
+ init_thread_vars()
280
+ {
281
+ int res;
282
+
283
+ ATOMIC_STORE_BOOL(&_ok_to_sample, false);
284
+ ATOMIC_STORE_BOOL(&_in_signal_handler, false);
285
+ ATOMIC_STORE_INT16(&_start_frame_index, 0);
286
+ ATOMIC_STORE_INT16(&_start_trace_index, 0);
287
+ ATOMIC_STORE_INT16(&_cur_traces_num, 0);
288
+
289
+ _ruby_thread = rb_thread_current(); // Used as a check to avoid any Fiber switching silliness
290
+
291
+ _traces = ALLOC_N(struct c_trace, MAX_TRACES); // TODO Check return
292
+
293
+ _gc_hook = Data_Wrap_Struct(rb_cObject, &scoutprof_gc_mark, NULL, &_traces);
294
+ rb_gc_register_address(&_gc_hook);
295
+
296
+ res = pthread_atfork(scout_parent_atfork_prepare, scout_parent_atfork_finish, NULL);
297
+ if (res != 0) {
298
+ fprintf(stderr, "APM-DEBUG: Pthread_atfork failed: %d\n", res);
299
+ }
300
+
301
+ #ifdef __linux__
302
+ // Create timer to target this thread
303
+ _sev.sigev_notify = SIGEV_THREAD_ID;
304
+ _sev.sigev_signo = SIGALRM;
305
+ _sev.sigev_notify_thread_id = syscall(SYS_gettid);
306
+ _sev.sigev_value.sival_ptr = &_timerid;
307
+ if (timer_create(CLOCK_MONOTONIC, &_sev, &_timerid) == -1) {
308
+ fprintf(stderr, "APM-DEBUG: Time create failed: %d\n", errno);
309
+ }
310
+ #endif
311
+
312
+ return;
313
+ }
314
+
315
+ /*
316
+ * Signal handler for each thread. Invoked from a signal when a job is run within Ruby's postponed_job queue
317
+ */
318
+ static void
319
+ scout_profile_broadcast_signal_handler(int sig)
320
+ {
321
+ int register_result;
322
+
323
+ if (ATOMIC_LOAD(&_ok_to_sample) == false) return;
324
+
325
+ if (ATOMIC_LOAD(&_in_signal_handler) == true) {
326
+ ATOMIC_ADD(&_skipped_in_signal_handler, 1);
327
+ return;
328
+ }
329
+
330
+
331
+ ATOMIC_STORE_BOOL(&_in_signal_handler, true);
332
+
333
+ if (rb_during_gc()) {
334
+ ATOMIC_ADD(&_skipped_in_gc, 1);
335
+ } else if (rb_thread_current() != _ruby_thread) {
336
+ ATOMIC_ADD(&_skipped_in_not_running, 1);
337
+ } else {
338
+ if (ATOMIC_LOAD(&_job_registered) == false){
339
+ register_result = rb_postponed_job_register(0, scout_record_sample, 0);
340
+ if ((register_result == 1) || (register_result == 2)) {
341
+ ATOMIC_STORE_BOOL(&_job_registered, true);
342
+ } else {
343
+ ATOMIC_ADD(&_skipped_in_job_registered, 1);
344
+ }
345
+ } // !_job_registered
346
+ }
347
+
348
+ ATOMIC_STORE_BOOL(&_in_signal_handler, false);
349
+ }
350
+
351
+ /*
352
+ * scout_record_sample: Defered function run from the per-thread handler
353
+ *
354
+ * Note: that this is called from *EVERY PROFILED THREAD FOR EACH CLOCK TICK
355
+ * INTERVAL*, so the performance of this method is crucial.
356
+ *
357
+ */
358
+ void
359
+ scout_record_sample()
360
+ {
361
+ int num_frames;
362
+ uint_fast16_t cur_traces_num, start_frame_index;
363
+
364
+ if (ATOMIC_LOAD(&_ok_to_sample) == false) return;
365
+ if (rb_during_gc()) {
366
+ ATOMIC_ADD(&_skipped_in_gc, 1);
367
+ return;
368
+ }
369
+ if (rb_thread_current() != _ruby_thread) {
370
+ ATOMIC_ADD(&_skipped_in_not_running, 1);
371
+ return;
372
+ }
373
+
374
+ cur_traces_num = ATOMIC_LOAD(&_cur_traces_num);
375
+ start_frame_index = ATOMIC_LOAD(&_start_frame_index);
376
+
377
+ if (cur_traces_num < MAX_TRACES) {
378
+ num_frames = rb_profile_frames(0, sizeof(_traces[cur_traces_num].frames_buf) / sizeof(VALUE), _traces[cur_traces_num].frames_buf, _traces[cur_traces_num].lines_buf);
379
+ if (num_frames - start_frame_index > 2) {
380
+ _traces[cur_traces_num].num_tracelines = num_frames - start_frame_index - 2; // The extra -2 is because there's a bug when reading the very first (bottom) 2 iseq objects for some reason
381
+ ATOMIC_ADD(&_cur_traces_num, 1);
382
+ } // TODO: add an else with a counter so we can track if we skipped profiling here
383
+ }
384
+ ATOMIC_STORE_BOOL(&_job_registered, false);
385
+ }
386
+
387
+ /* rb_scout_profile_frames: retreive the traces for the layer that is exiting
388
+ *
389
+ * Note: Calls to this must have already stopped sampling
390
+ */
391
+ static VALUE rb_scout_profile_frames(VALUE self)
392
+ {
393
+ int n;
394
+ uint_fast16_t i, cur_traces_num, start_trace_index;
395
+ VALUE traces, trace, trace_line;
396
+
397
+ if (ATOMIC_LOAD(&_thread_registered) == false) {
398
+ fprintf(stderr, "APM-DEBUG: Error: trying to get profiled frames on a non-profiled thread!\n");
399
+ ATOMIC_STORE_INT16(&_cur_traces_num, 0);
400
+ return rb_ary_new();
401
+ }
402
+
403
+ cur_traces_num = ATOMIC_LOAD(&_cur_traces_num);
404
+ start_trace_index = ATOMIC_LOAD(&_start_trace_index);
405
+
406
+ if (cur_traces_num - start_trace_index > 0) {
407
+ traces = rb_ary_new2(cur_traces_num - start_trace_index);
408
+ for(i = start_trace_index; i < cur_traces_num; i++) {
409
+ if (_traces[i].num_tracelines > 0) {
410
+ trace = rb_ary_new2(_traces[i].num_tracelines);
411
+ for(n = 0; n < _traces[i].num_tracelines; n++) {
412
+ if (TYPE(_traces[i].frames_buf[n]) == VALID_RUBY_FRAME) { // We should always get valid frames from rb_profile_frames, but that doesn't always seem to be the case
413
+ trace_line = rb_ary_new2(2);
414
+ rb_ary_store(trace_line, 0, _traces[i].frames_buf[n]);
415
+ rb_ary_store(trace_line, 1, INT2FIX(_traces[i].lines_buf[n]));
416
+ rb_ary_push(trace, trace_line);
417
+ } else {
418
+ fprintf(stderr, "APM-DEBUG: Non-data frame is: 0x%04x\n", TYPE(_traces[i].frames_buf[n]));
419
+ }
420
+ }
421
+ rb_ary_push(traces, trace);
422
+ }
423
+ }
424
+ } else {
425
+ traces = rb_ary_new();
426
+ }
427
+ ATOMIC_STORE_INT16(&_cur_traces_num, start_trace_index);
428
+ return traces;
429
+ }
430
+
431
+
432
+
433
+ /*****************************************************/
434
+ /* Control code */
435
+ /*****************************************************/
436
+
437
+ static void
438
+ scout_start_thread_timer()
439
+ {
440
+ #ifdef __linux__
441
+ struct itimerspec its;
442
+ sigset_t mask;
443
+ #endif
444
+
445
+ #ifdef __linux__
446
+ if (ATOMIC_LOAD(&_thread_registered) == false) return;
447
+
448
+ sigemptyset(&mask);
449
+ sigaddset(&mask, SIGALRM);
450
+ if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) {
451
+ fprintf(stderr, "APM-DEBUG: Block mask failed: %d\n", errno);
452
+ }
453
+
454
+ its.it_value.tv_sec = 0;
455
+ its.it_value.tv_nsec = INTERVAL;
456
+ its.it_interval.tv_sec = its.it_value.tv_sec;
457
+ its.it_interval.tv_nsec = its.it_value.tv_nsec;
458
+
459
+ if (timer_settime(_timerid, 0, &its, NULL) == -1) {
460
+ fprintf(stderr, "APM-DEBUG: Timer set failed in start sampling: %d\n", errno);
461
+ }
462
+
463
+ if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) {
464
+ fprintf(stderr, "APM-DEBUG: UNBlock mask failed: %d\n", errno);
465
+ }
466
+ #endif
467
+ }
468
+
469
+ static void
470
+ scout_stop_thread_timer()
471
+ {
472
+ #ifdef __linux__
473
+ struct itimerspec its;
474
+ #endif
475
+
476
+ if (ATOMIC_LOAD(&_thread_registered) == false) return;
477
+
478
+ #ifdef __linux__
479
+ memset((void*)&its, 0, sizeof(its));
480
+ if (timer_settime(_timerid, 0, &its, NULL) == -1 ) {
481
+ fprintf(stderr, "APM-DEBUG: Timer set failed: %d\n", errno);
482
+ }
483
+ #endif
484
+ }
485
+
486
+ /* Per thread start sampling */
487
+ static VALUE
488
+ rb_scout_start_sampling(VALUE self)
489
+ {
490
+ scout_add_profiled_thread(pthread_self());
491
+ ATOMIC_STORE_BOOL(&_ok_to_sample, true);
492
+ #ifdef __linux__
493
+ scout_start_thread_timer();
494
+ #endif
495
+ return Qtrue;
496
+ }
497
+
498
+ /* Per thread stop sampling */
499
+ static VALUE
500
+ rb_scout_stop_sampling(VALUE self, VALUE reset)
501
+ {
502
+ if(ATOMIC_LOAD(&_ok_to_sample) == true ) {
503
+ #ifdef __linux__
504
+ scout_stop_thread_timer();
505
+ #endif
506
+ }
507
+
508
+ ATOMIC_STORE_BOOL(&_ok_to_sample, false);
509
+
510
+ // TODO: I think this can be (reset == Qtrue)
511
+ if (TYPE(reset) == T_TRUE) {
512
+ ATOMIC_STORE_BOOL(&_job_registered, 0);
513
+ ATOMIC_STORE_BOOL(&_in_signal_handler, 0);
514
+ ATOMIC_STORE_INT16(&_start_trace_index, 0);
515
+ ATOMIC_STORE_INT16(&_start_frame_index, 0);
516
+ ATOMIC_STORE_INT16(&_cur_traces_num, 0);
517
+ ATOMIC_STORE_INT32(&_skipped_in_gc, 0);
518
+ ATOMIC_STORE_INT32(&_skipped_in_signal_handler, 0);
519
+ ATOMIC_STORE_INT32(&_skipped_in_job_registered, 0);
520
+ ATOMIC_STORE_INT32(&_skipped_in_not_running, 0);
521
+ }
522
+ return Qtrue;
523
+ }
524
+
525
+ // rb_scout_update_indexes: Called when each layer starts or something
526
+ static VALUE
527
+ rb_scout_update_indexes(VALUE self, VALUE frame_index, VALUE trace_index)
528
+ {
529
+ ATOMIC_STORE_INT16(&_start_trace_index, NUM2INT(trace_index));
530
+ ATOMIC_STORE_INT16(&_start_frame_index, NUM2INT(frame_index));
531
+ return Qtrue;
532
+ }
533
+
534
+ // rb_scout_current_trace_index: Get the current top of the trace stack
535
+ static VALUE
536
+ rb_scout_current_trace_index(VALUE self)
537
+ {
538
+ return INT2NUM(ATOMIC_LOAD(&_cur_traces_num));
539
+ }
540
+
541
+ // rb_scout_current_trace_index: Get the current top of the trace stack
542
+ static VALUE
543
+ rb_scout_current_frame_index(VALUE self)
544
+ {
545
+ int num_frames;
546
+ VALUE frames_buf[BUF_SIZE];
547
+ int lines_buf[BUF_SIZE];
548
+ num_frames = rb_profile_frames(0, sizeof(frames_buf) / sizeof(VALUE), frames_buf, lines_buf);
549
+ if (num_frames > 1) {
550
+ return INT2NUM(num_frames - 1);
551
+ } else {
552
+ return INT2NUM(0);
553
+ }
554
+ }
555
+
556
+
557
+
558
+ static VALUE
559
+ rb_scout_skipped_in_gc(VALUE self)
560
+ {
561
+ return INT2NUM(ATOMIC_LOAD(&_skipped_in_gc));
562
+ }
563
+
564
+ static VALUE
565
+ rb_scout_skipped_in_handler(VALUE self)
566
+ {
567
+ return INT2NUM(ATOMIC_LOAD(&_skipped_in_signal_handler));
568
+ }
569
+
570
+ static VALUE
571
+ rb_scout_skipped_in_job_registered(VALUE self)
572
+ {
573
+ return INT2NUM(ATOMIC_LOAD(&_skipped_in_job_registered));
574
+ }
575
+
576
+ static VALUE
577
+ rb_scout_skipped_in_not_running(VALUE self)
578
+ {
579
+ return INT2NUM(ATOMIC_LOAD(&_skipped_in_not_running));
580
+ }
581
+
582
+ ////////////////////////////////////////////////////////////////
583
+ // Fetch details from a frame
584
+ ////////////////////////////////////////////////////////////////
585
+
586
+ static VALUE
587
+ rb_scout_frame_klass(VALUE self, VALUE frame)
588
+ {
589
+ return rb_profile_frame_classpath(frame);
590
+ }
591
+
592
+ static VALUE
593
+ rb_scout_frame_method(VALUE self, VALUE frame)
594
+ {
595
+ return rb_profile_frame_label(frame);
596
+ }
597
+
598
+ static VALUE
599
+ rb_scout_frame_file(VALUE self, VALUE frame)
600
+ {
601
+ return rb_profile_frame_absolute_path(frame);
602
+ }
603
+
604
+ static VALUE
605
+ rb_scout_frame_lineno(VALUE self, VALUE frame)
606
+ {
607
+ return rb_profile_frame_first_lineno(frame);
608
+ }
609
+
610
+ ////////////////////////////////////////////////////////////////
611
+ ////////////////////////////////////////////////////////////////
612
+
613
+ ////////////////////////////////////////////////////////////////
614
+ ////////////////////////////////////////////////////////////////
615
+
616
+ // Gem Init. Set up constants, attach methods
617
+ void Init_stacks()
618
+ {
619
+ mScoutApm = rb_define_module("ScoutApm");
620
+ mInstruments = rb_define_module_under(mScoutApm, "Instruments");
621
+ cStacks = rb_define_class_under(mInstruments, "Stacks", rb_cObject);
622
+
623
+ rb_warning("Initializing ScoutProf Native Extension");
624
+
625
+ // Installs/uninstalls the signal handler.
626
+ rb_define_singleton_method(cStacks, "install", rb_scout_install_profiling, 0);
627
+ rb_define_singleton_method(cStacks, "uninstall", rb_scout_uninstall_profiling, 0);
628
+
629
+ rb_define_singleton_method(cStacks, "start", rb_scout_start_profiling, 0);
630
+
631
+ rb_define_singleton_method(cStacks, "add_profiled_thread", rb_scout_add_profiled_thread, 0);
632
+ rb_define_singleton_method(cStacks, "remove_profiled_thread", rb_scout_remove_profiled_thread, 0);
633
+
634
+ rb_define_singleton_method(cStacks, "profile_frames", rb_scout_profile_frames, 0);
635
+ rb_define_singleton_method(cStacks, "start_sampling", rb_scout_start_sampling, 0);
636
+ rb_define_singleton_method(cStacks, "stop_sampling", rb_scout_stop_sampling, 1);
637
+ rb_define_singleton_method(cStacks, "update_indexes", rb_scout_update_indexes, 2);
638
+ rb_define_singleton_method(cStacks, "current_trace_index", rb_scout_current_trace_index, 0);
639
+ rb_define_singleton_method(cStacks, "current_frame_index", rb_scout_current_frame_index, 0);
640
+
641
+ rb_define_singleton_method(cStacks, "frame_klass", rb_scout_frame_klass, 1);
642
+ rb_define_singleton_method(cStacks, "frame_method", rb_scout_frame_method, 1);
643
+ rb_define_singleton_method(cStacks, "frame_file", rb_scout_frame_file, 1);
644
+ rb_define_singleton_method(cStacks, "frame_lineno", rb_scout_frame_lineno, 1);
645
+
646
+ rb_define_singleton_method(cStacks, "skipped_in_gc", rb_scout_skipped_in_gc, 0);
647
+ rb_define_singleton_method(cStacks, "skipped_in_handler", rb_scout_skipped_in_handler, 0);
648
+ rb_define_singleton_method(cStacks, "skipped_in_job_registered", rb_scout_skipped_in_job_registered, 0);
649
+ rb_define_singleton_method(cStacks, "skipped_in_not_running", rb_scout_skipped_in_not_running, 0);
650
+
651
+ rb_define_const(cStacks, "ENABLED", Qtrue);
652
+ rb_warning("Finished Initializing ScoutProf Native Extension");
653
+ }
654
+
655
+ #else
656
+
657
+ static VALUE rb_scout_install_profiling(VALUE module)
658
+ {
659
+ return Qnil;
660
+ }
661
+
662
+ static VALUE rb_scout_uninstall_profiling(VALUE module)
663
+ {
664
+ return Qnil;
665
+ }
666
+
667
+ static VALUE rb_scout_start_profiling(VALUE module)
668
+ {
669
+ return Qnil;
670
+ }
671
+
672
+ static VALUE rb_scout_stop_profiling(VALUE module)
673
+ {
674
+ return Qnil;
675
+ }
676
+
677
+ static VALUE rb_scout_add_profiled_thread(VALUE module)
678
+ {
679
+ return Qnil;
680
+ }
681
+
682
+ static VALUE rb_scout_remove_profiled_thread(VALUE module)
683
+ {
684
+ return Qnil;
685
+ }
686
+
687
+ static VALUE rb_scout_profile_frames(VALUE self)
688
+ {
689
+ return rb_ary_new();
690
+ }
691
+
692
+ static VALUE
693
+ rb_scout_start_sampling(VALUE self)
694
+ {
695
+ return Qtrue;
696
+ }
697
+
698
+ static VALUE
699
+ rb_scout_stop_sampling(VALUE self)
700
+ {
701
+ return Qtrue;
702
+ }
703
+
704
+ static VALUE
705
+ rb_scout_update_indexes(VALUE self, VALUE frame_index, VALUE trace_index)
706
+ {
707
+ return Qtrue;
708
+ }
709
+
710
+ // rb_scout_current_trace_index: Get the current top of the trace stack
711
+ static VALUE
712
+ rb_scout_current_trace_index(VALUE self)
713
+ {
714
+ return INT2NUM(0);
715
+ }
716
+
717
+ // rb_scout_current_trace_index: Get the current top of the trace stack
718
+ static VALUE
719
+ rb_scout_current_frame_index(VALUE self)
720
+ {
721
+ return INT2NUM(0);
722
+ }
723
+
724
+ static VALUE
725
+ rb_scout_skipped_in_gc(VALUE self)
726
+ {
727
+ return INT2NUM(0);
728
+ }
729
+
730
+ static VALUE
731
+ rb_scout_skipped_in_handler(VALUE self)
732
+ {
733
+ return INT2NUM(0);
734
+ }
735
+
736
+ static VALUE
737
+ rb_scout_skipped_in_job_registered(VALUE self)
738
+ {
739
+ return INT2NUM(0);
740
+ }
741
+
742
+ static VALUE
743
+ rb_scout_skipped_in_not_running(VALUE self)
744
+ {
745
+ return INT2NUM(0);
746
+ }
747
+
748
+ static VALUE
749
+ rb_scout_frame_klass(VALUE self, VALUE frame)
750
+ {
751
+ return Qnil;
752
+ }
753
+
754
+ static VALUE
755
+ rb_scout_frame_method(VALUE self, VALUE frame)
756
+ {
757
+ return Qnil;
758
+ }
759
+
760
+ static VALUE
761
+ rb_scout_frame_file(VALUE self, VALUE frame)
762
+ {
763
+ return Qnil;
764
+ }
765
+
766
+ static VALUE
767
+ rb_scout_frame_lineno(VALUE self, VALUE frame)
768
+ {
769
+ return Qnil;
770
+ }
771
+
772
+ void Init_stacks()
773
+ {
774
+ mScoutApm = rb_define_module("ScoutApm");
775
+ mInstruments = rb_define_module_under(mScoutApm, "Instruments");
776
+ cStacks = rb_define_class_under(mInstruments, "Stacks", rb_cObject);
777
+
778
+ // Installs/uninstalls the signal handler.
779
+ rb_define_singleton_method(cStacks, "install", rb_scout_install_profiling, 0);
780
+ rb_define_singleton_method(cStacks, "uninstall", rb_scout_uninstall_profiling, 0);
781
+
782
+ // Starts/removes the timer tick, leaving the sighandler.
783
+ rb_define_singleton_method(cStacks, "start", rb_scout_start_profiling, 0);
784
+ rb_define_singleton_method(cStacks, "stop", rb_scout_stop_profiling, 0);
785
+
786
+ rb_define_singleton_method(cStacks, "add_profiled_thread", rb_scout_add_profiled_thread, 0);
787
+ rb_define_singleton_method(cStacks, "remove_profiled_thread", rb_scout_remove_profiled_thread, 0);
788
+
789
+ rb_define_singleton_method(cStacks, "profile_frames", rb_scout_profile_frames, 0);
790
+ rb_define_singleton_method(cStacks, "start_sampling", rb_scout_start_sampling, 0);
791
+ rb_define_singleton_method(cStacks, "stop_sampling", rb_scout_stop_sampling, 1);
792
+ rb_define_singleton_method(cStacks, "update_indexes", rb_scout_update_indexes, 2);
793
+ rb_define_singleton_method(cStacks, "current_trace_index", rb_scout_current_trace_index, 0);
794
+ rb_define_singleton_method(cStacks, "current_frame_index", rb_scout_current_frame_index, 0);
795
+
796
+ rb_define_singleton_method(cStacks, "frame_klass", rb_scout_frame_klass, 1);
797
+ rb_define_singleton_method(cStacks, "frame_method", rb_scout_frame_method, 1);
798
+ rb_define_singleton_method(cStacks, "frame_file", rb_scout_frame_file, 1);
799
+ rb_define_singleton_method(cStacks, "frame_lineno", rb_scout_frame_lineno, 1);
800
+
801
+ rb_define_singleton_method(cStacks, "skipped_in_gc", rb_scout_skipped_in_gc, 0);
802
+ rb_define_singleton_method(cStacks, "skipped_in_handler", rb_scout_skipped_in_handler, 0);
803
+ rb_define_singleton_method(cStacks, "skipped_in_job_registered", rb_scout_skipped_in_job_registered, 0);
804
+ rb_define_singleton_method(cStacks, "skipped_in_not_running", rb_scout_skipped_in_not_running, 0);
805
+
806
+ rb_define_const(cStacks, "ENABLED", Qfalse);
807
+ rb_define_const(cStacks, "INSTALLED", Qfalse);
808
+ }
809
+
810
+ #endif //#ifdef RUBY_INTERNAL_EVENT_NEWOBJ
811
+