scout_apm 2.1.32 → 2.2.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
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
+