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.
- 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
|
+
|