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.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/CHANGELOG.markdown +2 -161
- data/Rakefile +2 -2
- data/ext/allocations/allocations.c +0 -6
- data/ext/allocations/extconf.rb +0 -1
- data/ext/stacks/extconf.rb +33 -0
- data/ext/stacks/scout_atomics.h +86 -0
- data/ext/stacks/stacks.c +744 -0
- data/lib/scout_apm.rb +16 -24
- data/lib/scout_apm/agent.rb +38 -93
- data/lib/scout_apm/agent/logging.rb +1 -6
- data/lib/scout_apm/agent/reporting.rb +6 -8
- data/lib/scout_apm/app_server_load.rb +10 -21
- data/lib/scout_apm/attribute_arranger.rb +2 -0
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -71
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +27 -66
- data/lib/scout_apm/background_worker.rb +15 -19
- data/lib/scout_apm/capacity.rb +57 -0
- data/lib/scout_apm/config.rb +29 -135
- data/lib/scout_apm/context.rb +5 -9
- data/lib/scout_apm/deploy_integrations/capistrano_2.cap +12 -0
- data/lib/scout_apm/deploy_integrations/capistrano_2.rb +83 -0
- data/lib/scout_apm/deploy_integrations/capistrano_3.cap +12 -0
- data/lib/scout_apm/deploy_integrations/capistrano_3.rb +88 -0
- data/lib/scout_apm/environment.rb +15 -22
- data/lib/scout_apm/histogram.rb +2 -11
- data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
- data/lib/scout_apm/instant/middleware.rb +57 -198
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +2 -1
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +59 -90
- data/lib/scout_apm/instruments/active_record.rb +5 -7
- data/lib/scout_apm/instruments/delayed_job.rb +57 -0
- data/lib/scout_apm/instruments/grape.rb +3 -4
- data/lib/scout_apm/instruments/middleware_detailed.rb +6 -4
- data/lib/scout_apm/instruments/middleware_summary.rb +1 -39
- data/lib/scout_apm/instruments/mongoid.rb +3 -24
- data/lib/scout_apm/instruments/net_http.rb +2 -7
- data/lib/scout_apm/instruments/percentile_sampler.rb +19 -36
- data/lib/scout_apm/instruments/process/process_cpu.rb +2 -3
- data/lib/scout_apm/instruments/process/process_memory.rb +3 -3
- data/lib/scout_apm/layaway.rb +33 -76
- data/lib/scout_apm/layer.rb +59 -16
- data/lib/scout_apm/layer_converters/converter_base.rb +0 -199
- data/lib/scout_apm/layer_converters/job_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/metric_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +90 -15
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +101 -13
- data/lib/scout_apm/metric_set.rb +1 -9
- data/lib/scout_apm/metric_stats.rb +8 -8
- data/lib/scout_apm/reporter.rb +15 -51
- data/lib/scout_apm/request_histograms.rb +0 -4
- data/lib/scout_apm/request_manager.rb +1 -2
- data/lib/scout_apm/scored_item_set.rb +0 -7
- data/lib/scout_apm/serializers/deploy_serializer.rb +16 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +3 -9
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +5 -2
- data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +1 -2
- data/lib/scout_apm/server_integrations/puma.rb +2 -5
- data/lib/scout_apm/slow_item_set.rb +80 -0
- data/lib/scout_apm/slow_job_record.rb +1 -6
- data/lib/scout_apm/slow_transaction.rb +2 -20
- data/lib/scout_apm/store.rb +12 -50
- data/lib/scout_apm/trace_compactor.rb +311 -0
- data/lib/scout_apm/tracked_request.rb +37 -128
- data/lib/scout_apm/utils/backtrace_parser.rb +5 -7
- data/lib/scout_apm/utils/fake_stacks.rb +83 -0
- data/lib/scout_apm/version.rb +1 -1
- data/scout_apm.gemspec +4 -6
- data/test/test_helper.rb +0 -56
- data/test/unit/config_test.rb +9 -60
- data/test/unit/histogram_test.rb +0 -14
- data/test/unit/layaway_test.rb +16 -31
- data/test/unit/serializers/payload_serializer_test.rb +105 -3
- data/test/unit/slow_item_set_test.rb +94 -0
- data/test/unit/slow_job_policy_test.rb +49 -0
- data/test/unit/slow_request_policy_test.rb +5 -4
- data/test/unit/utils/backtrace_parser_test.rb +0 -19
- data/tester.rb +53 -0
- metadata +29 -124
- data/.rubocop.yml +0 -8
- data/Guardfile +0 -42
- data/ext/rusage/README.md +0 -26
- data/ext/rusage/extconf.rb +0 -5
- data/ext/rusage/rusage.c +0 -52
- data/lib/scout_apm/background_job_integrations/resque.rb +0 -85
- data/lib/scout_apm/background_recorder.rb +0 -43
- data/lib/scout_apm/debug.rb +0 -37
- data/lib/scout_apm/git_revision.rb +0 -51
- data/lib/scout_apm/instruments/action_view.rb +0 -49
- data/lib/scout_apm/instruments/resque.rb +0 -40
- data/lib/scout_apm/layer_children_set.rb +0 -77
- data/lib/scout_apm/limited_layer.rb +0 -122
- data/lib/scout_apm/rack.rb +0 -26
- data/lib/scout_apm/remote/message.rb +0 -23
- data/lib/scout_apm/remote/recorder.rb +0 -57
- data/lib/scout_apm/remote/router.rb +0 -49
- data/lib/scout_apm/remote/server.rb +0 -58
- data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +0 -21
- data/lib/scout_apm/synchronous_recorder.rb +0 -26
- data/lib/scout_apm/utils/gzip_helper.rb +0 -24
- data/lib/scout_apm/utils/numbers.rb +0 -14
- data/lib/scout_apm/utils/scm.rb +0 -14
- data/test/unit/background_job_integrations/sidekiq_test.rb +0 -104
- data/test/unit/context_test.rb +0 -30
- data/test/unit/git_revision_test.rb +0 -15
- data/test/unit/instruments/net_http_test.rb +0 -21
- data/test/unit/instruments/percentile_sampler_test.rb +0 -137
- data/test/unit/layer_children_set_test.rb +0 -88
- data/test/unit/limited_layer_test.rb +0 -53
- data/test/unit/remote/test_message.rb +0 -13
- data/test/unit/remote/test_router.rb +0 -33
- data/test/unit/remote/test_server.rb +0 -15
- data/test/unit/store_test.rb +0 -89
- data/test/unit/test_tracked_request.rb +0 -87
- data/test/unit/utils/numbers_test.rb +0 -15
- data/test/unit/utils/scm.rb +0 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a1a02cb2156a16b3ddaa054eb34065653ed2abb7
|
|
4
|
+
data.tar.gz: 8023afd5daf499a2441ee54b9d7c5914ec00ac73
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2483b8a46a365e9568f36f7d2c46b8271b295b9c12c950694318d4dd09668d91d30a39cd3c0b7c03396ce07e9a5bd9f1f77710d9cc3175cfb09d57e41aea8d28
|
|
7
|
+
data.tar.gz: 979cae85d0a830fe3c4ff4be8640f7bc503e1bceb72bb096ae731d7679bf93b2d17de266dd300a57b23029014ccabc61d20863fcd6d8b104b3c82e4b632c91d5
|
data/.gitignore
CHANGED
data/CHANGELOG.markdown
CHANGED
|
@@ -1,165 +1,6 @@
|
|
|
1
|
-
# 2.
|
|
1
|
+
# 2.2.0
|
|
2
2
|
|
|
3
|
-
*
|
|
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
|
|
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('
|
|
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
|
}
|
data/ext/allocations/extconf.rb
CHANGED
|
@@ -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
|
+
|
data/ext/stacks/stacks.c
ADDED
|
@@ -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
|
+
|