scout_apm 2.1.32 → 2.2.0.pre0

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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/CHANGELOG.markdown +2 -161
  4. data/Rakefile +2 -2
  5. data/ext/allocations/allocations.c +0 -6
  6. data/ext/allocations/extconf.rb +0 -1
  7. data/ext/stacks/extconf.rb +33 -0
  8. data/ext/stacks/scout_atomics.h +86 -0
  9. data/ext/stacks/stacks.c +744 -0
  10. data/lib/scout_apm.rb +16 -24
  11. data/lib/scout_apm/agent.rb +38 -93
  12. data/lib/scout_apm/agent/logging.rb +1 -6
  13. data/lib/scout_apm/agent/reporting.rb +6 -8
  14. data/lib/scout_apm/app_server_load.rb +10 -21
  15. data/lib/scout_apm/attribute_arranger.rb +2 -0
  16. data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -71
  17. data/lib/scout_apm/background_job_integrations/sidekiq.rb +27 -66
  18. data/lib/scout_apm/background_worker.rb +15 -19
  19. data/lib/scout_apm/capacity.rb +57 -0
  20. data/lib/scout_apm/config.rb +29 -135
  21. data/lib/scout_apm/context.rb +5 -9
  22. data/lib/scout_apm/deploy_integrations/capistrano_2.cap +12 -0
  23. data/lib/scout_apm/deploy_integrations/capistrano_2.rb +83 -0
  24. data/lib/scout_apm/deploy_integrations/capistrano_3.cap +12 -0
  25. data/lib/scout_apm/deploy_integrations/capistrano_3.rb +88 -0
  26. data/lib/scout_apm/environment.rb +15 -22
  27. data/lib/scout_apm/histogram.rb +2 -11
  28. data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
  29. data/lib/scout_apm/instant/middleware.rb +57 -198
  30. data/lib/scout_apm/instruments/action_controller_rails_2.rb +2 -1
  31. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +59 -90
  32. data/lib/scout_apm/instruments/active_record.rb +5 -7
  33. data/lib/scout_apm/instruments/delayed_job.rb +57 -0
  34. data/lib/scout_apm/instruments/grape.rb +3 -4
  35. data/lib/scout_apm/instruments/middleware_detailed.rb +6 -4
  36. data/lib/scout_apm/instruments/middleware_summary.rb +1 -39
  37. data/lib/scout_apm/instruments/mongoid.rb +3 -24
  38. data/lib/scout_apm/instruments/net_http.rb +2 -7
  39. data/lib/scout_apm/instruments/percentile_sampler.rb +19 -36
  40. data/lib/scout_apm/instruments/process/process_cpu.rb +2 -3
  41. data/lib/scout_apm/instruments/process/process_memory.rb +3 -3
  42. data/lib/scout_apm/layaway.rb +33 -76
  43. data/lib/scout_apm/layer.rb +59 -16
  44. data/lib/scout_apm/layer_converters/converter_base.rb +0 -199
  45. data/lib/scout_apm/layer_converters/job_converter.rb +1 -1
  46. data/lib/scout_apm/layer_converters/metric_converter.rb +1 -1
  47. data/lib/scout_apm/layer_converters/slow_job_converter.rb +90 -15
  48. data/lib/scout_apm/layer_converters/slow_request_converter.rb +101 -13
  49. data/lib/scout_apm/metric_set.rb +1 -9
  50. data/lib/scout_apm/metric_stats.rb +8 -8
  51. data/lib/scout_apm/reporter.rb +15 -51
  52. data/lib/scout_apm/request_histograms.rb +0 -4
  53. data/lib/scout_apm/request_manager.rb +1 -2
  54. data/lib/scout_apm/scored_item_set.rb +0 -7
  55. data/lib/scout_apm/serializers/deploy_serializer.rb +16 -0
  56. data/lib/scout_apm/serializers/payload_serializer.rb +3 -9
  57. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +5 -2
  58. data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +1 -2
  59. data/lib/scout_apm/server_integrations/puma.rb +2 -5
  60. data/lib/scout_apm/slow_item_set.rb +80 -0
  61. data/lib/scout_apm/slow_job_record.rb +1 -6
  62. data/lib/scout_apm/slow_transaction.rb +2 -20
  63. data/lib/scout_apm/store.rb +12 -50
  64. data/lib/scout_apm/trace_compactor.rb +311 -0
  65. data/lib/scout_apm/tracked_request.rb +37 -128
  66. data/lib/scout_apm/utils/backtrace_parser.rb +5 -7
  67. data/lib/scout_apm/utils/fake_stacks.rb +83 -0
  68. data/lib/scout_apm/version.rb +1 -1
  69. data/scout_apm.gemspec +4 -6
  70. data/test/test_helper.rb +0 -56
  71. data/test/unit/config_test.rb +9 -60
  72. data/test/unit/histogram_test.rb +0 -14
  73. data/test/unit/layaway_test.rb +16 -31
  74. data/test/unit/serializers/payload_serializer_test.rb +105 -3
  75. data/test/unit/slow_item_set_test.rb +94 -0
  76. data/test/unit/slow_job_policy_test.rb +49 -0
  77. data/test/unit/slow_request_policy_test.rb +5 -4
  78. data/test/unit/utils/backtrace_parser_test.rb +0 -19
  79. data/tester.rb +53 -0
  80. metadata +29 -124
  81. data/.rubocop.yml +0 -8
  82. data/Guardfile +0 -42
  83. data/ext/rusage/README.md +0 -26
  84. data/ext/rusage/extconf.rb +0 -5
  85. data/ext/rusage/rusage.c +0 -52
  86. data/lib/scout_apm/background_job_integrations/resque.rb +0 -85
  87. data/lib/scout_apm/background_recorder.rb +0 -43
  88. data/lib/scout_apm/debug.rb +0 -37
  89. data/lib/scout_apm/git_revision.rb +0 -51
  90. data/lib/scout_apm/instruments/action_view.rb +0 -49
  91. data/lib/scout_apm/instruments/resque.rb +0 -40
  92. data/lib/scout_apm/layer_children_set.rb +0 -77
  93. data/lib/scout_apm/limited_layer.rb +0 -122
  94. data/lib/scout_apm/rack.rb +0 -26
  95. data/lib/scout_apm/remote/message.rb +0 -23
  96. data/lib/scout_apm/remote/recorder.rb +0 -57
  97. data/lib/scout_apm/remote/router.rb +0 -49
  98. data/lib/scout_apm/remote/server.rb +0 -58
  99. data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +0 -21
  100. data/lib/scout_apm/synchronous_recorder.rb +0 -26
  101. data/lib/scout_apm/utils/gzip_helper.rb +0 -24
  102. data/lib/scout_apm/utils/numbers.rb +0 -14
  103. data/lib/scout_apm/utils/scm.rb +0 -14
  104. data/test/unit/background_job_integrations/sidekiq_test.rb +0 -104
  105. data/test/unit/context_test.rb +0 -30
  106. data/test/unit/git_revision_test.rb +0 -15
  107. data/test/unit/instruments/net_http_test.rb +0 -21
  108. data/test/unit/instruments/percentile_sampler_test.rb +0 -137
  109. data/test/unit/layer_children_set_test.rb +0 -88
  110. data/test/unit/limited_layer_test.rb +0 -53
  111. data/test/unit/remote/test_message.rb +0 -13
  112. data/test/unit/remote/test_router.rb +0 -33
  113. data/test/unit/remote/test_server.rb +0 -15
  114. data/test/unit/store_test.rb +0 -89
  115. data/test/unit/test_tracked_request.rb +0 -87
  116. data/test/unit/utils/numbers_test.rb +0 -15
  117. data/test/unit/utils/scm.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 13a92807ec823e335d8c008de7b2ee934f2b2a5a
4
- data.tar.gz: 0914f1345038c081b84dd22ee1b98f009cbd8bb5
3
+ metadata.gz: a1a02cb2156a16b3ddaa054eb34065653ed2abb7
4
+ data.tar.gz: 8023afd5daf499a2441ee54b9d7c5914ec00ac73
5
5
  SHA512:
6
- metadata.gz: 917cd00759988d396425711cc5471641edbf8c02a5434e2cea567a6d4bb1aea8fdb06b429950289631b7338432a528a9bd82033a154c6ccdf58a5ca835deed4d
7
- data.tar.gz: 958687024b4bc80bf5c7364196b0a06c8cfc61e3afce63777c0fa31a5203c6ba892a7089a58e0539d2a6976c53fae2f4557f47a279eea89718d0d9cdc2cb0dc1
6
+ metadata.gz: 2483b8a46a365e9568f36f7d2c46b8271b295b9c12c950694318d4dd09668d91d30a39cd3c0b7c03396ce07e9a5bd9f1f77710d9cc3175cfb09d57e41aea8d28
7
+ data.tar.gz: 979cae85d0a830fe3c4ff4be8640f7bc503e1bceb72bb096ae731d7679bf93b2d17de266dd300a57b23029014ccabc61d20863fcd6d8b104b3c82e4b632c91d5
data/.gitignore CHANGED
@@ -17,4 +17,4 @@ test/tmp/*coverage/*
17
17
  coverage/*
18
18
  lib/*.bundle
19
19
  lib/*.so
20
- log/scout_apm.log
20
+ **/.RUBYARCHDIR.time
data/CHANGELOG.markdown CHANGED
@@ -1,165 +1,6 @@
1
- # 2.1.32
1
+ # 2.2.0
2
2
 
3
- * Better naming when using Resque + ActiveJob
4
- * Better naming when using Sidekiq + DelayedExtension
5
-
6
- # 2.1.31
7
-
8
- * Better detection of Resque queue names
9
- * Fix passing arguments through Active Record instrumentation. (Thanks to Nick Quaranto for providing the fix)
10
- * Stricter checks to prevent agent from starting in Rails console
11
-
12
- # 2.1.30
13
-
14
- * Add Resque support.
15
-
16
- # 2.1.29
17
-
18
- * Add `scm_subdirectory` option. Useful for when your app code does not live in your SCM root directory.
19
-
20
- # 2.1.28
21
-
22
- * Changes to app server load data
23
-
24
- # 2.1.27
25
-
26
- * Don't attempt to call `current_layer.type` on nil
27
-
28
- # 2.1.26
29
-
30
- * Bug fix [4b188d6](https://github.com/scoutapp/scout_apm_ruby/commit/4b188d698852c86b86d8768ea5b37d706ce544fe)
31
-
32
- # 2.1.25
33
-
34
- * Automatically instrument API and Metal controllers.
35
-
36
- # 2.1.24
37
-
38
- * Capture additional layers of application backtrace frames. (From 3 -> 8)
39
-
40
- # 2.1.23
41
-
42
- * Extend Mongoid instrumentation to 6.x
43
-
44
- # 2.1.22
45
-
46
- * Add DevTrace support for newest 4.2.x and 5.x versions of Rails
47
-
48
- # 2.1.21
49
-
50
- * Fix edge case, causing DevTrace to fail
51
- * Add debug tooling, allowing custom functions to be inserted into the agent at
52
- key points.
53
-
54
- # 2.1.20
55
-
56
- * Add a `detailed_middleware` boolean configuration option to capture
57
- per-middleware data, as opposed to the default of aggregating all middleware
58
- together. This has a small amount of additional overhead, approximately
59
- 10-15ms per request.
60
-
61
- # 2.1.19
62
-
63
- * Log all configuration settings at start when log level is debug
64
- * Tune DelayedJob class name detection
65
-
66
- # 2.1.18
67
-
68
- * Max layaway file threshold limit
69
-
70
- # 2.1.17
71
-
72
- * Additional logging around file system usage
73
-
74
- # 2.1.16
75
-
76
- * Extract the name correctly for DelayedJob workers run via ActiveJob
77
-
78
- # 2.1.15
79
-
80
- * Limit memory usage for very long running requests.
81
-
82
- # 2.1.14
83
-
84
- * Add TrackedRequest#ignore_request! to entirely ignore and stop capturing a
85
- certain request. Use in your code by calling:
86
- ScoutApm::RequestManager.lookup.ignore_request!
87
-
88
- # 2.1.13
89
-
90
- * Rework Delayed Job instrumentation to not interfere with other instruments.
91
-
92
- # 2.1.12
93
-
94
- * Revert 2.1.11's Delayed Job change - caused issues in a handful of environments
95
-
96
- # 2.1.11
97
-
98
- * Support alternate methods of launching Delayed Job
99
-
100
- # 2.1.10
101
-
102
- * Fix issue getting a default Application Name when it wasn't explicitly set
103
-
104
- # 2.1.9
105
-
106
- * Send raw histograms of response time, enabling more accurate 95th %iles
107
- * Raw histograms are used in Apdex calculations
108
- * Gzip payloads
109
- * Fix Mongoid (5.0) + Mongo (2.1) support
110
- * Initial Delayed Job support
111
- * Limit max metric size of a trace to 500.
112
-
113
-
114
- # 2.1.8
115
-
116
- * Adds Git revision detection, which is reported on app load and associated with transaction traces
117
-
118
- # 2.1.7
119
-
120
- * Fix allocations extension compilation on Ruby 1.8.7
121
-
122
- # 2.1.6
123
-
124
- * Support older versions of Grape (0.10 onward)
125
- * Vendor rusage library
126
- * Fix double-exit that caused error messages when running under Passenger
127
-
128
- # 2.1.5
129
-
130
- * Be less strict loading Rails environments that don't have a matching
131
- scout_apm.yml section. Previously we raised, now we log and continue
132
- to load with the ENV settings
133
- * Fix a memory leak in error recovery code
134
- * Fix a minor race condition in data coordination between processes.
135
- * There was a tiny sliver of a window where a lock wasn't held, and it caused
136
- an exception to be raised.
137
-
138
- # 2.1.4
139
-
140
- * Enhance regular expression that determines if a backtrace line is "inside"
141
- the application
142
- * Avoids labeling vendor/ as part of the monitored app
143
-
144
- # 2.1.3
145
-
146
- * Less noisy output on errors with Context
147
- * Not logging errors w/nil keys or values
148
- * Bumping log level down from WARN => INFO on errors
149
- * Fix error with complicated AR queries
150
- * Caused high log noise
151
- * Sidekiq instrumentation changes to handle a variety of edge cases
152
-
153
- # 2.1.2
154
-
155
- * Applies `Rails.application.config.filter_parameters` settings to reported transaction trace uris
156
- * Fix incompatibility with ResqueWeb and middleware instrumentation
157
-
158
- # 2.1.1
159
-
160
- * Fix an issue with AR instrumentation and complex queries
161
- * Fix use of configuration option `data_file`
162
- * Update unit tests
3
+ * ScoutProf BETA
163
4
 
164
5
  # 2.1.0
165
6
 
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require "bundler/gem_tasks"
3
3
  task :default => :test
4
4
 
5
5
  desc "Run Unit Tests"
6
- task :test => [:compile] do
6
+ task :test do
7
7
  $: << File.expand_path(File.dirname(__FILE__) + "/test")
8
8
  Dir.glob('./test/**/*_test.rb').each { |file| require file }
9
9
  end
@@ -21,5 +21,5 @@ end
21
21
  # Rake Compiler
22
22
  require 'rake/extensiontask'
23
23
  Rake::ExtensionTask.new('allocations')
24
- Rake::ExtensionTask.new('rusage')
24
+ Rake::ExtensionTask.new('stacks')
25
25
 
@@ -1,8 +1,4 @@
1
- #ifdef HAVE_RUBY_RUBY_H
2
1
  #include <ruby/ruby.h>
3
- #else // Ruby <= 1.8.7
4
- #include <ruby.h>
5
- #endif
6
2
 
7
3
  VALUE mScoutApm;
8
4
  VALUE mInstruments;
@@ -56,7 +52,6 @@ void Init_allocations()
56
52
  mInstruments = rb_define_module_under(mScoutApm, "Instruments");
57
53
  cAllocations = rb_define_class_under(mInstruments, "Allocations", rb_cObject);
58
54
  rb_define_singleton_method(cAllocations, "count", get_allocation_count, 0);
59
- rb_define_singleton_method(cAllocations, "count", get_allocation_count, 0);
60
55
  rb_define_const(cAllocations, "ENABLED", Qtrue);
61
56
  Init_hooks(mScoutApm);
62
57
  }
@@ -79,7 +74,6 @@ void Init_allocations()
79
74
  mInstruments = rb_define_module_under(mScoutApm, "Instruments");
80
75
  cAllocations = rb_define_class_under(mInstruments, "Allocations", rb_cObject);
81
76
  rb_define_singleton_method(cAllocations, "count", get_allocation_count, 0);
82
- rb_define_singleton_method(cAllocations, "count", get_allocation_count, 0);
83
77
  rb_define_const(cAllocations, "ENABLED", Qfalse);
84
78
  Init_hooks(mScoutApm);
85
79
  }
@@ -1,4 +1,3 @@
1
1
  require 'mkmf'
2
2
 
3
- have_header("ruby/ruby.h") # Needed to check for Ruby <= 1.8.7
4
3
  create_makefile('allocations')
@@ -0,0 +1,33 @@
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
+ can_compile &&= have_library('rt') # for timer_create, timer_settime
15
+
16
+ # Pick the atomics implementation
17
+ has_atomics_header = have_header("stdatomic.h")
18
+ if has_atomics_header
19
+ $defs.push "-DSCOUT_USE_NEW_ATOMICS"
20
+ else
21
+ $defs.push "-DSCOUT_USE_OLD_ATOMICS"
22
+ end
23
+
24
+ if can_compile
25
+ create_makefile('stacks')
26
+ else
27
+ # Create a dummy Makefile, to satisfy Gem::Installer#install
28
+ mfile = open("Makefile", "wb")
29
+ mfile.puts '.PHONY: install'
30
+ mfile.puts 'install:'
31
+ mfile.puts "\t" + '@echo "Stack extension not installed, skipping."'
32
+ mfile.close
33
+ end
@@ -0,0 +1,86 @@
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
+
@@ -0,0 +1,744 @@
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 <semaphore.h>
28
+ #include <setjmp.h>
29
+ #include <signal.h>
30
+ #include <stdbool.h>
31
+
32
+ /*
33
+ * System
34
+ */
35
+ #include <sys/syscall.h>
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
+ #define NANO_SECOND_MULTIPLIER 1000000 // 1 millisecond = 1,000,000 Nanoseconds
61
+ const long INTERVAL = 1 * NANO_SECOND_MULTIPLIER; // milliseconds * NANO_SECOND_MULTIPLIER
62
+
63
+ // For support of thread id in timer_create
64
+ #define sigev_notify_thread_id _sigev_un._tid
65
+
66
+ #ifdef RUBY_INTERNAL_EVENT_NEWOBJ
67
+
68
+ // Forward Declarations
69
+ static void init_thread_vars();
70
+ static void scout_profile_broadcast_signal_handler(int sig);
71
+ void scout_record_sample();
72
+ static void scout_start_thread_timer();
73
+ static void scout_stop_thread_timer();
74
+
75
+ ////////////////////////////////////////////////////////////////////////////////////////
76
+ // Per-Thread variables
77
+ ////////////////////////////////////////////////////////////////////////////////////////
78
+
79
+ struct c_trace {
80
+ int num_tracelines;
81
+ int lines_buf[BUF_SIZE];
82
+ VALUE frames_buf[BUF_SIZE];
83
+ };
84
+
85
+ static __thread struct c_trace *_traces;
86
+
87
+ static __thread atomic_bool_t _thread_registered = ATOMIC_INIT(false);
88
+ static __thread atomic_bool_t _ok_to_sample = ATOMIC_INIT(false);
89
+ static __thread atomic_bool_t _in_signal_handler = ATOMIC_INIT(false);
90
+
91
+ static __thread atomic_uint16_t _start_frame_index = ATOMIC_INIT(0);
92
+ static __thread atomic_uint16_t _start_trace_index = ATOMIC_INIT(0);
93
+ static __thread atomic_uint16_t _cur_traces_num = ATOMIC_INIT(0);
94
+
95
+ static __thread atomic_uint32_t _skipped_in_gc = ATOMIC_INIT(0);
96
+ static __thread atomic_uint32_t _skipped_in_signal_handler = ATOMIC_INIT(0);
97
+ static __thread atomic_uint32_t _skipped_in_job_registered = ATOMIC_INIT(0);
98
+
99
+ static __thread VALUE _gc_hook;
100
+
101
+ static __thread atomic_bool_t _job_registered = ATOMIC_INIT(false);
102
+
103
+ static __thread timer_t _timerid;
104
+ static __thread struct sigevent _sev;
105
+
106
+
107
+ ////////////////////////////////////////////////////////////////////////////////////////
108
+ // Global variables
109
+ ////////////////////////////////////////////////////////////////////////////////////////
110
+
111
+ static int
112
+ scout_add_profiled_thread(pthread_t th)
113
+ {
114
+ if (ATOMIC_LOAD(&_thread_registered) == true) return 1;
115
+
116
+ init_thread_vars();
117
+ ATOMIC_STORE_BOOL(&_thread_registered, true);
118
+
119
+ fprintf(stderr, "APM DEBUG: Added thread id: %li\n", (unsigned long int)pthread_self());
120
+ return 1;
121
+ }
122
+
123
+ /*
124
+ * rb_scout_add_profiled_thread: adds the currently running thread to the head of the linked list
125
+ *
126
+ * Initializes thread locals:
127
+ * - ok_to_sample to false
128
+ * - start_frame_index and start_trace_index to 0
129
+ * - cur_traces_num to 0
130
+ */
131
+ static VALUE
132
+ rb_scout_add_profiled_thread(VALUE self)
133
+ {
134
+ scout_add_profiled_thread(pthread_self());
135
+ return Qtrue;
136
+ }
137
+
138
+ /*
139
+ * remove_profiled_thread: removes a thread from the linked list.
140
+ * if the linked list is empty, this is a noop
141
+ */
142
+ static int
143
+ remove_profiled_thread(pthread_t th)
144
+ {
145
+ if (ATOMIC_LOAD(&_thread_registered) == false) return 1;
146
+
147
+ ATOMIC_STORE_BOOL(&_ok_to_sample, false);
148
+
149
+ fprintf(stderr, "APM DEBUG: Would remove thread id: %li\n", (unsigned long int)th);
150
+
151
+ // Unregister the _gc_hook from Ruby ObjectSpace, then free it as well as the _traces struct it wrapped.
152
+ rb_gc_unregister_address(&_gc_hook);
153
+ xfree(&_gc_hook);
154
+ xfree(&_traces);
155
+
156
+ timer_delete(_timerid);
157
+
158
+ ATOMIC_STORE_BOOL(&_thread_registered, false);
159
+ return 0;
160
+ }
161
+
162
+ /* rb_scout_remove_profiled_thread: removes a thread from the linked list
163
+ *
164
+ */
165
+ static VALUE rb_scout_remove_profiled_thread(VALUE self)
166
+ {
167
+ remove_profiled_thread(pthread_self());
168
+ return Qtrue;
169
+ }
170
+
171
+
172
+ /* rb_scout_start_profiling: Installs the global timer
173
+ */
174
+ static VALUE
175
+ rb_scout_start_profiling(VALUE self)
176
+ {
177
+ if (scout_profiling_running) {
178
+ return Qtrue;
179
+ }
180
+
181
+ scout_profiling_running = 1;
182
+
183
+ return Qtrue;
184
+ }
185
+
186
+ /* rb_scout_uninstall_profiling: called when ruby is shutting down.
187
+ * NOTE: If ever this method should be called where Ruby should continue to run, we need to free our
188
+ * memory allocated in each profiled thread.
189
+ */
190
+ static VALUE
191
+ rb_scout_uninstall_profiling(VALUE self)
192
+ {
193
+ return Qnil;
194
+ }
195
+
196
+ static VALUE
197
+ rb_scout_install_profiling(VALUE self)
198
+ {
199
+ struct sigaction new_vtaction, old_vtaction;
200
+
201
+ // We can only install once. If uninstall is called, we will NOT be able to call install again.
202
+ // Instead, stop/start should be used to temporarily disable all ScoutProf sampling.
203
+ if (scout_profiling_installed) {
204
+ return Qfalse;
205
+ }
206
+
207
+ // Also set up an interrupt handler for when we broadcast an alarm
208
+ new_vtaction.sa_handler = scout_profile_broadcast_signal_handler;
209
+ new_vtaction.sa_flags = SA_RESTART;
210
+ sigemptyset(&new_vtaction.sa_mask);
211
+ sigaction(SIGALRM, &new_vtaction, &old_vtaction);
212
+
213
+ rb_define_const(cStacks, "INSTALLED", Qtrue);
214
+ scout_profiling_installed = 1;
215
+
216
+ return Qtrue;
217
+ }
218
+
219
+ ////////////////////////////////////////////////////////////////////////////////////////
220
+ // Per-Thread Handler
221
+ ////////////////////////////////////////////////////////////////////////////////////////
222
+
223
+ static void
224
+ scoutprof_gc_mark(void *data)
225
+ {
226
+ uint_fast16_t i;
227
+ int n;
228
+ for (i = 0; i < ATOMIC_LOAD(&_cur_traces_num); i++) {
229
+ for (n = 0; n < _traces[i].num_tracelines; n++) {
230
+ rb_gc_mark(_traces[i].frames_buf[n]);
231
+ }
232
+ }
233
+ }
234
+
235
+ static void
236
+ scout_parent_atfork_prepare()
237
+ {
238
+ // TODO: Should we track how much time the fork took?
239
+ if (ATOMIC_LOAD(&_ok_to_sample) == true) {
240
+ scout_stop_thread_timer();
241
+ }
242
+ }
243
+
244
+ static void
245
+ scout_parent_atfork_finish()
246
+ {
247
+ if (ATOMIC_LOAD(&_ok_to_sample) == true) {
248
+ scout_start_thread_timer();
249
+ }
250
+ }
251
+
252
+ static void
253
+ init_thread_vars()
254
+ {
255
+ int res;
256
+
257
+ ATOMIC_STORE_BOOL(&_ok_to_sample, false);
258
+ ATOMIC_STORE_BOOL(&_in_signal_handler, false);
259
+ ATOMIC_STORE_INT16(&_start_frame_index, 0);
260
+ ATOMIC_STORE_INT16(&_start_trace_index, 0);
261
+ ATOMIC_STORE_INT16(&_cur_traces_num, 0);
262
+
263
+ _traces = ALLOC_N(struct c_trace, MAX_TRACES); // TODO Check return
264
+
265
+ _gc_hook = Data_Wrap_Struct(rb_cObject, &scoutprof_gc_mark, NULL, &_traces);
266
+ rb_gc_register_address(&_gc_hook);
267
+
268
+ res = pthread_atfork(scout_parent_atfork_prepare, scout_parent_atfork_finish, NULL);
269
+ if (res != 0) {
270
+ fprintf(stderr, "Pthread_atfork failed: %d\n", res);
271
+ }
272
+
273
+ // Create timer to target this thread
274
+ _sev.sigev_notify = SIGEV_THREAD_ID;
275
+ _sev.sigev_signo = SIGALRM;
276
+ _sev.sigev_notify_thread_id = syscall(SYS_gettid);
277
+ _sev.sigev_value.sival_ptr = &_timerid;
278
+ if (timer_create(CLOCK_MONOTONIC, &_sev, &_timerid) == -1) {
279
+ fprintf(stderr, "Time create failed: %d\n", errno);
280
+ }
281
+
282
+ return;
283
+ }
284
+
285
+ /*
286
+ * Signal handler for each thread. Invoked from a signal when a job is run within Ruby's postponed_job queue
287
+ */
288
+ static void
289
+ scout_profile_broadcast_signal_handler(int sig)
290
+ {
291
+ int register_result;
292
+
293
+ if (ATOMIC_LOAD(&_ok_to_sample) == false) return;
294
+
295
+ if (ATOMIC_LOAD(&_in_signal_handler) == true) {
296
+ ATOMIC_ADD(&_skipped_in_signal_handler, 1);
297
+ return;
298
+ }
299
+
300
+
301
+ ATOMIC_STORE_BOOL(&_in_signal_handler, true);
302
+
303
+ if (rb_during_gc()) {
304
+ ATOMIC_ADD(&_skipped_in_gc, 1);
305
+ } else {
306
+ if (ATOMIC_LOAD(&_job_registered) == false){
307
+ register_result = rb_postponed_job_register(0, scout_record_sample, 0);
308
+ if ((register_result == 1) || (register_result == 2)) {
309
+ ATOMIC_STORE_BOOL(&_job_registered, true);
310
+ } else {
311
+ ATOMIC_ADD(&_skipped_in_job_registered, 1);
312
+ }
313
+ } // !_job_registered
314
+ }
315
+
316
+ ATOMIC_STORE_BOOL(&_in_signal_handler, false);
317
+ }
318
+
319
+ /*
320
+ * scout_record_sample: Defered function run from the per-thread handler
321
+ *
322
+ * Note: that this is called from *EVERY PROFILED THREAD FOR EACH CLOCK TICK
323
+ * INTERVAL*, so the performance of this method is crucial.
324
+ *
325
+ */
326
+ void
327
+ scout_record_sample()
328
+ {
329
+ int num_frames;
330
+ uint_fast16_t cur_traces_num, start_frame_index;
331
+
332
+ if (ATOMIC_LOAD(&_ok_to_sample) == false) return;
333
+ if (rb_during_gc()) {
334
+ ATOMIC_ADD(&_skipped_in_gc, 1);
335
+ return;
336
+ }
337
+
338
+ cur_traces_num = ATOMIC_LOAD(&_cur_traces_num);
339
+ start_frame_index = ATOMIC_LOAD(&_start_frame_index);
340
+
341
+ if (cur_traces_num < MAX_TRACES) {
342
+ 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);
343
+ if (num_frames - start_frame_index > 2) {
344
+ _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
345
+ ATOMIC_ADD(&_cur_traces_num, 1);
346
+ } // TODO: add an else with a counter so we can track if we skipped profiling here
347
+ }
348
+ ATOMIC_STORE_BOOL(&_job_registered, false);
349
+ }
350
+
351
+ /* rb_scout_profile_frames: retreive the traces for the layer that is exiting
352
+ *
353
+ * Note: Calls to this must have already stopped sampling
354
+ */
355
+ static VALUE rb_scout_profile_frames(VALUE self)
356
+ {
357
+ int n;
358
+ uint_fast16_t i, cur_traces_num, start_trace_index;
359
+ VALUE traces, trace, trace_line;
360
+
361
+ if (ATOMIC_LOAD(&_thread_registered) == false) {
362
+ fprintf(stderr, "Error: trying to get profiled frames on a non-profiled thread!\n");
363
+ ATOMIC_STORE_INT16(&_cur_traces_num, 0);
364
+ return rb_ary_new();
365
+ }
366
+
367
+ cur_traces_num = ATOMIC_LOAD(&_cur_traces_num);
368
+ start_trace_index = ATOMIC_LOAD(&_start_trace_index);
369
+
370
+ if (cur_traces_num - start_trace_index > 0) {
371
+ traces = rb_ary_new2(cur_traces_num - start_trace_index);
372
+ for(i = start_trace_index; i < cur_traces_num; i++) {
373
+ if (_traces[i].num_tracelines > 0) {
374
+ trace = rb_ary_new2(_traces[i].num_tracelines);
375
+ for(n = 0; n < _traces[i].num_tracelines; n++) {
376
+ trace_line = rb_ary_new2(2);
377
+ rb_ary_store(trace_line, 0, _traces[i].frames_buf[n]);
378
+ rb_ary_store(trace_line, 1, INT2FIX(_traces[i].lines_buf[n]));
379
+ rb_ary_push(trace, trace_line);
380
+ }
381
+ rb_ary_push(traces, trace);
382
+ }
383
+ }
384
+ } else {
385
+ traces = rb_ary_new();
386
+ }
387
+ ATOMIC_STORE_INT16(&_cur_traces_num, start_trace_index);
388
+ return traces;
389
+ }
390
+
391
+
392
+
393
+ /*****************************************************/
394
+ /* Control code */
395
+ /*****************************************************/
396
+
397
+ static void
398
+ scout_start_thread_timer()
399
+ {
400
+ struct itimerspec its;
401
+ sigset_t mask;
402
+
403
+ if (ATOMIC_LOAD(&_thread_registered) == false) return;
404
+
405
+ sigemptyset(&mask);
406
+ sigaddset(&mask, SIGALRM);
407
+ if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) {
408
+ fprintf(stderr, "Block mask failed: %d\n", errno);
409
+ }
410
+
411
+ its.it_value.tv_sec = 0;
412
+ its.it_value.tv_nsec = INTERVAL;
413
+ its.it_interval.tv_sec = its.it_value.tv_sec;
414
+ its.it_interval.tv_nsec = its.it_value.tv_nsec;
415
+
416
+ if (timer_settime(_timerid, 0, &its, NULL) == -1) {
417
+ fprintf(stderr, "Timer set failed in start sampling: %d\n", errno);
418
+ }
419
+
420
+ if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) {
421
+ fprintf(stderr, "UNBlock mask failed: %d\n", errno);
422
+ }
423
+ }
424
+
425
+ static void
426
+ scout_stop_thread_timer()
427
+ {
428
+ struct itimerspec its;
429
+
430
+ if (ATOMIC_LOAD(&_thread_registered) == false) return;
431
+
432
+ memset((void*)&its, 0, sizeof(its));
433
+ if (timer_settime(_timerid, 0, &its, NULL) == -1 ) {
434
+ fprintf(stderr, "Timer set failed: %d\n", errno);
435
+ }
436
+ }
437
+
438
+ /* Per thread start sampling */
439
+ static VALUE
440
+ rb_scout_start_sampling(VALUE self)
441
+ {
442
+ scout_add_profiled_thread(pthread_self());
443
+ ATOMIC_STORE_BOOL(&_ok_to_sample, true);
444
+ scout_start_thread_timer();
445
+ return Qtrue;
446
+ }
447
+
448
+ /* Per thread stop sampling */
449
+ static VALUE
450
+ rb_scout_stop_sampling(VALUE self, VALUE reset)
451
+ {
452
+ if(ATOMIC_LOAD(&_ok_to_sample) == true ) {
453
+ scout_stop_thread_timer();
454
+ }
455
+
456
+ ATOMIC_STORE_BOOL(&_ok_to_sample, false);
457
+
458
+ // TODO: I think this can be (reset == Qtrue)
459
+ if (TYPE(reset) == T_TRUE) {
460
+ ATOMIC_STORE_BOOL(&_job_registered, 0);
461
+ ATOMIC_STORE_BOOL(&_in_signal_handler, 0);
462
+ ATOMIC_STORE_INT16(&_start_trace_index, 0);
463
+ ATOMIC_STORE_INT16(&_start_frame_index, 0);
464
+ ATOMIC_STORE_INT16(&_cur_traces_num, 0);
465
+ ATOMIC_STORE_INT32(&_skipped_in_gc, 0);
466
+ ATOMIC_STORE_INT32(&_skipped_in_signal_handler, 0);
467
+ ATOMIC_STORE_INT32(&_skipped_in_job_registered, 0);
468
+ }
469
+ return Qtrue;
470
+ }
471
+
472
+ // rb_scout_update_indexes: Called when each layer starts or something
473
+ static VALUE
474
+ rb_scout_update_indexes(VALUE self, VALUE frame_index, VALUE trace_index)
475
+ {
476
+ ATOMIC_STORE_INT16(&_start_trace_index, NUM2INT(trace_index));
477
+ ATOMIC_STORE_INT16(&_start_frame_index, NUM2INT(frame_index));
478
+ return Qtrue;
479
+ }
480
+
481
+ // rb_scout_current_trace_index: Get the current top of the trace stack
482
+ static VALUE
483
+ rb_scout_current_trace_index(VALUE self)
484
+ {
485
+ return INT2NUM(ATOMIC_LOAD(&_cur_traces_num));
486
+ }
487
+
488
+ // rb_scout_current_trace_index: Get the current top of the trace stack
489
+ static VALUE
490
+ rb_scout_current_frame_index(VALUE self)
491
+ {
492
+ int num_frames;
493
+ VALUE frames_buf[BUF_SIZE];
494
+ int lines_buf[BUF_SIZE];
495
+ num_frames = rb_profile_frames(0, sizeof(frames_buf) / sizeof(VALUE), frames_buf, lines_buf);
496
+ if (num_frames > 1) {
497
+ return INT2NUM(num_frames - 1);
498
+ } else {
499
+ return INT2NUM(0);
500
+ }
501
+ }
502
+
503
+
504
+
505
+ static VALUE
506
+ rb_scout_skipped_in_gc(VALUE self)
507
+ {
508
+ return INT2NUM(ATOMIC_LOAD(&_skipped_in_gc));
509
+ }
510
+
511
+ static VALUE
512
+ rb_scout_skipped_in_handler(VALUE self)
513
+ {
514
+ return INT2NUM(ATOMIC_LOAD(&_skipped_in_signal_handler));
515
+ }
516
+
517
+ static VALUE
518
+ rb_scout_skipped_in_job_registered(VALUE self)
519
+ {
520
+ return INT2NUM(ATOMIC_LOAD(&_skipped_in_job_registered));
521
+ }
522
+
523
+ ////////////////////////////////////////////////////////////////
524
+ // Fetch details from a frame
525
+ ////////////////////////////////////////////////////////////////
526
+
527
+ static VALUE
528
+ rb_scout_frame_klass(VALUE self, VALUE frame)
529
+ {
530
+ return rb_profile_frame_classpath(frame);
531
+ }
532
+
533
+ static VALUE
534
+ rb_scout_frame_method(VALUE self, VALUE frame)
535
+ {
536
+ return rb_profile_frame_label(frame);
537
+ }
538
+
539
+ static VALUE
540
+ rb_scout_frame_file(VALUE self, VALUE frame)
541
+ {
542
+ return rb_profile_frame_absolute_path(frame);
543
+ }
544
+
545
+ static VALUE
546
+ rb_scout_frame_lineno(VALUE self, VALUE frame)
547
+ {
548
+ return rb_profile_frame_first_lineno(frame);
549
+ }
550
+
551
+ ////////////////////////////////////////////////////////////////
552
+ ////////////////////////////////////////////////////////////////
553
+
554
+ ////////////////////////////////////////////////////////////////
555
+ ////////////////////////////////////////////////////////////////
556
+
557
+ // Gem Init. Set up constants, attach methods
558
+ void Init_stacks()
559
+ {
560
+ mScoutApm = rb_define_module("ScoutApm");
561
+ mInstruments = rb_define_module_under(mScoutApm, "Instruments");
562
+ cStacks = rb_define_class_under(mInstruments, "Stacks", rb_cObject);
563
+
564
+ rb_warning("Initializing ScoutProf Native Extension");
565
+
566
+ // Installs/uninstalls the signal handler.
567
+ rb_define_singleton_method(cStacks, "install", rb_scout_install_profiling, 0);
568
+ rb_define_singleton_method(cStacks, "uninstall", rb_scout_uninstall_profiling, 0);
569
+
570
+ rb_define_singleton_method(cStacks, "start", rb_scout_start_profiling, 0);
571
+
572
+ rb_define_singleton_method(cStacks, "add_profiled_thread", rb_scout_add_profiled_thread, 0);
573
+ rb_define_singleton_method(cStacks, "remove_profiled_thread", rb_scout_remove_profiled_thread, 0);
574
+
575
+ rb_define_singleton_method(cStacks, "profile_frames", rb_scout_profile_frames, 0);
576
+ rb_define_singleton_method(cStacks, "start_sampling", rb_scout_start_sampling, 0);
577
+ rb_define_singleton_method(cStacks, "stop_sampling", rb_scout_stop_sampling, 1);
578
+ rb_define_singleton_method(cStacks, "update_indexes", rb_scout_update_indexes, 2);
579
+ rb_define_singleton_method(cStacks, "current_trace_index", rb_scout_current_trace_index, 0);
580
+ rb_define_singleton_method(cStacks, "current_frame_index", rb_scout_current_frame_index, 0);
581
+
582
+ rb_define_singleton_method(cStacks, "frame_klass", rb_scout_frame_klass, 1);
583
+ rb_define_singleton_method(cStacks, "frame_method", rb_scout_frame_method, 1);
584
+ rb_define_singleton_method(cStacks, "frame_file", rb_scout_frame_file, 1);
585
+ rb_define_singleton_method(cStacks, "frame_lineno", rb_scout_frame_lineno, 1);
586
+
587
+ rb_define_singleton_method(cStacks, "skipped_in_gc", rb_scout_skipped_in_gc, 0);
588
+ rb_define_singleton_method(cStacks, "skipped_in_handler", rb_scout_skipped_in_handler, 0);
589
+ rb_define_singleton_method(cStacks, "skipped_in_job_registered", rb_scout_skipped_in_job_registered, 0);
590
+
591
+ rb_define_const(cStacks, "ENABLED", Qtrue);
592
+ rb_warning("Finished Initializing ScoutProf Native Extension");
593
+ }
594
+
595
+ #else
596
+
597
+ static VALUE rb_scout_install_profiling(VALUE module)
598
+ {
599
+ return Qnil;
600
+ }
601
+
602
+ static VALUE rb_scout_uninstall_profiling(VALUE module)
603
+ {
604
+ return Qnil;
605
+ }
606
+
607
+ static VALUE rb_scout_start_profiling(VALUE module)
608
+ {
609
+ return Qnil;
610
+ }
611
+
612
+ static VALUE rb_scout_stop_profiling(VALUE module)
613
+ {
614
+ return Qnil;
615
+ }
616
+
617
+ static VALUE rb_scout_add_profiled_thread(VALUE module)
618
+ {
619
+ return Qnil;
620
+ }
621
+
622
+ static VALUE rb_scout_remove_profiled_thread(VALUE module)
623
+ {
624
+ return Qnil;
625
+ }
626
+
627
+ static VALUE rb_scout_profile_frames(VALUE self)
628
+ {
629
+ return rb_ary_new();
630
+ }
631
+
632
+ static VALUE
633
+ rb_scout_start_sampling(VALUE self)
634
+ {
635
+ return Qtrue;
636
+ }
637
+
638
+ static VALUE
639
+ rb_scout_stop_sampling(VALUE self)
640
+ {
641
+ return Qtrue;
642
+ }
643
+
644
+ static VALUE
645
+ rb_scout_update_indexes(VALUE self, VALUE frame_index, VALUE trace_index)
646
+ {
647
+ return Qtrue;
648
+ }
649
+
650
+ // rb_scout_current_trace_index: Get the current top of the trace stack
651
+ static VALUE
652
+ rb_scout_current_trace_index(VALUE self)
653
+ {
654
+ return INT2NUM(0);
655
+ }
656
+
657
+ // rb_scout_current_trace_index: Get the current top of the trace stack
658
+ static VALUE
659
+ rb_scout_current_frame_index(VALUE self)
660
+ {
661
+ return INT2NUM(0);
662
+ }
663
+
664
+ static VALUE
665
+ rb_scout_skipped_in_gc(VALUE self)
666
+ {
667
+ return INT2NUM(0);
668
+ }
669
+
670
+ static VALUE
671
+ rb_scout_skipped_in_handler(VALUE self)
672
+ {
673
+ return INT2NUM(0);
674
+ }
675
+
676
+ static VALUE
677
+ rb_scout_skipped_in_job_registered(VALUE self)
678
+ {
679
+ return INT2NUM(0);
680
+ }
681
+
682
+ static VALUE
683
+ rb_scout_frame_klass(VALUE self, VALUE frame)
684
+ {
685
+ return Qnil;
686
+ }
687
+
688
+ static VALUE
689
+ rb_scout_frame_method(VALUE self, VALUE frame)
690
+ {
691
+ return Qnil;
692
+ }
693
+
694
+ static VALUE
695
+ rb_scout_frame_file(VALUE self, VALUE frame)
696
+ {
697
+ return Qnil;
698
+ }
699
+
700
+ static VALUE
701
+ rb_scout_frame_lineno(VALUE self, VALUE frame)
702
+ {
703
+ return Qnil;
704
+ }
705
+
706
+ void Init_stacks()
707
+ {
708
+ mScoutApm = rb_define_module("ScoutApm");
709
+ mInstruments = rb_define_module_under(mScoutApm, "Instruments");
710
+ cStacks = rb_define_class_under(mInstruments, "Stacks", rb_cObject);
711
+
712
+ // Installs/uninstalls the signal handler.
713
+ rb_define_singleton_method(cStacks, "install", rb_scout_install_profiling, 0);
714
+ rb_define_singleton_method(cStacks, "uninstall", rb_scout_uninstall_profiling, 0);
715
+
716
+ // Starts/removes the timer tick, leaving the sighandler.
717
+ rb_define_singleton_method(cStacks, "start", rb_scout_start_profiling, 0);
718
+ rb_define_singleton_method(cStacks, "stop", rb_scout_stop_profiling, 0);
719
+
720
+ rb_define_singleton_method(cStacks, "add_profiled_thread", rb_scout_add_profiled_thread, 0);
721
+ rb_define_singleton_method(cStacks, "remove_profiled_thread", rb_scout_remove_profiled_thread, 0);
722
+
723
+ rb_define_singleton_method(cStacks, "profile_frames", rb_scout_profile_frames, 0);
724
+ rb_define_singleton_method(cStacks, "start_sampling", rb_scout_start_sampling, 0);
725
+ rb_define_singleton_method(cStacks, "stop_sampling", rb_scout_stop_sampling, 1);
726
+ rb_define_singleton_method(cStacks, "update_indexes", rb_scout_update_indexes, 2);
727
+ rb_define_singleton_method(cStacks, "current_trace_index", rb_scout_current_trace_index, 0);
728
+ rb_define_singleton_method(cStacks, "current_frame_index", rb_scout_current_frame_index, 0);
729
+
730
+ rb_define_singleton_method(cStacks, "frame_klass", rb_scout_frame_klass, 1);
731
+ rb_define_singleton_method(cStacks, "frame_method", rb_scout_frame_method, 1);
732
+ rb_define_singleton_method(cStacks, "frame_file", rb_scout_frame_file, 1);
733
+ rb_define_singleton_method(cStacks, "frame_lineno", rb_scout_frame_lineno, 1);
734
+
735
+ rb_define_singleton_method(cStacks, "skipped_in_gc", rb_scout_skipped_in_gc, 0);
736
+ rb_define_singleton_method(cStacks, "skipped_in_handler", rb_scout_skipped_in_handler, 0);
737
+ rb_define_singleton_method(cStacks, "skipped_in_job_registered", rb_scout_skipped_in_job_registered, 0);
738
+
739
+ rb_define_const(cStacks, "ENABLED", Qfalse);
740
+ rb_define_const(cStacks, "INSTALLED", Qfalse);
741
+ }
742
+
743
+ #endif //#ifdef RUBY_INTERNAL_EVENT_NEWOBJ
744
+