scout_apm 3.0.0.pre28 → 4.0.0

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