scout_apm 0.1.18.stackprof4 → 0.9.0
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/CHANGELOG.markdown +5 -1
- data/lib/scout_apm.rb +0 -21
- data/lib/scout_apm/agent.rb +0 -9
- data/lib/scout_apm/config.rb +7 -11
- data/lib/scout_apm/environment.rb +2 -15
- data/lib/scout_apm/instruments/action_controller_rails_3.rb +10 -22
- data/lib/scout_apm/reporter.rb +2 -21
- data/lib/scout_apm/slow_transaction.rb +29 -42
- data/lib/scout_apm/store.rb +7 -15
- data/lib/scout_apm/tracer.rb +3 -15
- data/lib/scout_apm/version.rb +1 -1
- data/scout_apm.gemspec +0 -2
- metadata +4 -25
- data/lib/scout_apm/deploy_integrations/capistrano_2.cap +0 -12
- data/lib/scout_apm/deploy_integrations/capistrano_2.rb +0 -83
- data/lib/scout_apm/deploy_integrations/capistrano_3.cap +0 -12
- data/lib/scout_apm/deploy_integrations/capistrano_3.rb +0 -82
- data/lib/scout_apm/serializers/deploy_serializer.rb +0 -16
- data/lib/scout_apm/stackprof_tree_collapser.rb +0 -218
- data/lib/scout_apm/utils/fake_stack_prof.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 359e7ebbd4d2be52175b519c7eed26801f332cf6
|
4
|
+
data.tar.gz: d54ba2ef21fc61b38e54136637bd97391ba0c26b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5c7c714ffa299176340a4b6d1c62f248106840565245c1ecef1696e103218f3c91dcd2e31d3f0164bf97d125f36f6312c4f4db1c3875dc6c4a2a9b3d12c3420
|
7
|
+
data.tar.gz: 45cabecb512ef84af5ab618d227a787d92f64e62874352c770d70a8b8ccf23facf992b30a0ce7ded385b291bf44f11858a5cf3b5f5544d63766fa34fb504ab7b
|
data/CHANGELOG.markdown
CHANGED
data/lib/scout_apm.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
end
|
3
3
|
|
4
|
-
#####################################
|
5
|
-
# Ruby StdLibrary Requires
|
6
|
-
#####################################
|
7
4
|
require 'cgi'
|
8
5
|
require 'logger'
|
9
6
|
require 'net/http'
|
@@ -13,18 +10,6 @@ require 'socket'
|
|
13
10
|
require 'yaml'
|
14
11
|
require 'thread'
|
15
12
|
|
16
|
-
#####################################
|
17
|
-
# Gem Requires
|
18
|
-
#####################################
|
19
|
-
begin
|
20
|
-
require 'stackprof'
|
21
|
-
rescue LoadError
|
22
|
-
require 'scout_apm/utils/fake_stack_prof'
|
23
|
-
end
|
24
|
-
|
25
|
-
#####################################
|
26
|
-
# Internal Requires
|
27
|
-
#####################################
|
28
13
|
require 'scout_apm/version'
|
29
14
|
|
30
15
|
require 'scout_apm/server_integrations/passenger'
|
@@ -40,9 +25,6 @@ require 'scout_apm/framework_integrations/rails_3_or_4'
|
|
40
25
|
require 'scout_apm/framework_integrations/sinatra'
|
41
26
|
require 'scout_apm/framework_integrations/ruby'
|
42
27
|
|
43
|
-
require 'scout_apm/deploy_integrations/capistrano_3'
|
44
|
-
#require 'scout_apm/deploy_integrations/capistrano_2'
|
45
|
-
|
46
28
|
require 'scout_apm/instruments/net_http'
|
47
29
|
require 'scout_apm/instruments/moped'
|
48
30
|
require 'scout_apm/instruments/mongoid'
|
@@ -59,7 +41,6 @@ require 'scout_apm/utils/sql_sanitizer'
|
|
59
41
|
require 'scout_apm/utils/null_logger'
|
60
42
|
require 'scout_apm/utils/installed_gems'
|
61
43
|
require 'scout_apm/utils/time'
|
62
|
-
|
63
44
|
require 'scout_apm/config'
|
64
45
|
require 'scout_apm/environment'
|
65
46
|
require 'scout_apm/agent'
|
@@ -75,14 +56,12 @@ require 'scout_apm/stack_item'
|
|
75
56
|
require 'scout_apm/store'
|
76
57
|
require 'scout_apm/tracer'
|
77
58
|
require 'scout_apm/context'
|
78
|
-
require 'scout_apm/stackprof_tree_collapser'
|
79
59
|
require 'scout_apm/slow_transaction'
|
80
60
|
require 'scout_apm/capacity'
|
81
61
|
|
82
62
|
require 'scout_apm/serializers/payload_serializer'
|
83
63
|
require 'scout_apm/serializers/directive_serializer'
|
84
64
|
require 'scout_apm/serializers/app_server_load_serializer'
|
85
|
-
require 'scout_apm/serializers/deploy_serializer'
|
86
65
|
|
87
66
|
if defined?(Rails) and Rails.respond_to?(:version) and Rails.version >= '3'
|
88
67
|
module ScoutApm
|
data/lib/scout_apm/agent.rb
CHANGED
@@ -78,11 +78,6 @@ module ScoutApm
|
|
78
78
|
init_logger
|
79
79
|
logger.info "Attempting to start Scout Agent [#{ScoutApm::VERSION}] on [#{environment.hostname}]"
|
80
80
|
|
81
|
-
if environment.deploy_integration
|
82
|
-
logger.info "Starting monitoring for [#{environment.deploy_integration.name}]]."
|
83
|
-
return environment.deploy_integration.install
|
84
|
-
end
|
85
|
-
|
86
81
|
return false unless preconditions_met?
|
87
82
|
|
88
83
|
@started = true
|
@@ -202,9 +197,5 @@ module ScoutApm
|
|
202
197
|
@installed_instruments << instance
|
203
198
|
instance.install
|
204
199
|
end
|
205
|
-
|
206
|
-
def deploy_integration
|
207
|
-
environment.deploy_integration
|
208
|
-
end
|
209
200
|
end
|
210
201
|
end
|
data/lib/scout_apm/config.rb
CHANGED
@@ -21,7 +21,6 @@ module ScoutApm
|
|
21
21
|
DEFAULTS = {
|
22
22
|
'host' => 'https://apm.scoutapp.com',
|
23
23
|
'log_level' => 'info',
|
24
|
-
'stackprof_interval' => 20000 # microseconds, 1000 = 1 millisecond, so 20k == 20 milliseconds
|
25
24
|
}.freeze
|
26
25
|
|
27
26
|
def initialize(config_path = nil)
|
@@ -36,10 +35,11 @@ module ScoutApm
|
|
36
35
|
# all, and only read off the ENV var this is useful to break a loop during
|
37
36
|
# boot, where we needed an option to set the application root.
|
38
37
|
def value(key, env_only=false)
|
39
|
-
value =
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
value = if env_only
|
39
|
+
ENV['SCOUT_'+key.upcase]
|
40
|
+
else
|
41
|
+
ENV['SCOUT_'+key.upcase] || settings[key]
|
42
|
+
end
|
43
43
|
|
44
44
|
value.to_s.strip.length.zero? ? nil : value
|
45
45
|
end
|
@@ -54,12 +54,8 @@ module ScoutApm
|
|
54
54
|
File.expand_path(config_path)
|
55
55
|
end
|
56
56
|
|
57
|
-
def
|
58
|
-
settings
|
59
|
-
end
|
60
|
-
|
61
|
-
def settings(try_reload=false)
|
62
|
-
(@settings.nil? || try_reload) ? @settings = load_file : @settings
|
57
|
+
def settings
|
58
|
+
@settings ||= load_file
|
63
59
|
end
|
64
60
|
|
65
61
|
def config_environment
|
@@ -24,13 +24,8 @@ module ScoutApm
|
|
24
24
|
ScoutApm::FrameworkIntegrations::Ruby.new, # Fallback if none match
|
25
25
|
]
|
26
26
|
|
27
|
-
DEPLOY_INTEGRATIONS = [
|
28
|
-
ScoutApm::DeployIntegrations::Capistrano3.new(Logger.new(STDOUT)),
|
29
|
-
# ScoutApm::DeployIntegrations::Capistrano2.new(Logger.new(STDOUT)),
|
30
|
-
]
|
31
|
-
|
32
27
|
def env
|
33
|
-
@env ||=
|
28
|
+
@env ||= framework_integration.env
|
34
29
|
end
|
35
30
|
|
36
31
|
def framework
|
@@ -63,7 +58,6 @@ module ScoutApm
|
|
63
58
|
end
|
64
59
|
|
65
60
|
def root
|
66
|
-
return deploy_integration.root if deploy_integration
|
67
61
|
framework_root
|
68
62
|
end
|
69
63
|
|
@@ -71,6 +65,7 @@ module ScoutApm
|
|
71
65
|
if override_root = Agent.instance.config.value("application_root", true)
|
72
66
|
return override_root
|
73
67
|
end
|
68
|
+
|
74
69
|
if framework == :rails
|
75
70
|
RAILS_ROOT.to_s
|
76
71
|
elsif framework == :rails3_or_4
|
@@ -111,14 +106,6 @@ module ScoutApm
|
|
111
106
|
app_server_integration.forking?
|
112
107
|
end
|
113
108
|
|
114
|
-
def deploy_integration
|
115
|
-
@deploy_integration ||= DEPLOY_INTEGRATIONS.detect{ |integration| integration.present? }
|
116
|
-
end
|
117
|
-
|
118
|
-
def deploy_integration?
|
119
|
-
!@deploy_integration.nil?
|
120
|
-
end
|
121
|
-
|
122
109
|
### ruby checks
|
123
110
|
|
124
111
|
def rubinius?
|
@@ -27,25 +27,12 @@ module ScoutApm
|
|
27
27
|
|
28
28
|
if defined?(::ActionView) && defined?(::ActionView::PartialRenderer)
|
29
29
|
ScoutApm::Agent.instance.logger.debug "Instrumenting ActionView::PartialRenderer"
|
30
|
-
|
30
|
+
ActionView::PartialRenderer.class_eval do
|
31
31
|
include ScoutApm::Tracer
|
32
|
-
instrument_method :render_partial,
|
33
|
-
:metric_name => 'View/#{@template.virtual_path rescue "Unknown Partial"}/Rendering',
|
34
|
-
:scope => true
|
35
|
-
|
36
|
-
instrument_method :collection_with_template,
|
37
|
-
:metric_name => 'View/#{@template.virtual_path rescue "Unknown Collection"}/Rendering',
|
38
|
-
:scope => true
|
39
|
-
end
|
40
|
-
|
41
|
-
ScoutApm::Agent.instance.logger.debug "Instrumenting ActionView::TemplateRenderer"
|
42
|
-
::ActionView::TemplateRenderer.class_eval do
|
43
|
-
include ScoutApm::Tracer
|
44
|
-
instrument_method :render_template,
|
45
|
-
:metric_name => 'View/#{args[0].virtual_path rescue "Unknown"}/Rendering',
|
46
|
-
:scope => true
|
32
|
+
instrument_method :render_partial, :metric_name => 'View/#{@template.virtual_path}/Rendering', :scope => true
|
47
33
|
end
|
48
34
|
end
|
35
|
+
|
49
36
|
end
|
50
37
|
end
|
51
38
|
|
@@ -55,18 +42,13 @@ module ScoutApm
|
|
55
42
|
scout_controller_action = "Controller/#{controller_path}/#{action_name}"
|
56
43
|
|
57
44
|
self.class.scout_apm_trace(scout_controller_action, :uri => request.fullpath, :ip => request.remote_ip) do
|
58
|
-
Thread::current[:scout_apm_prof] = nil
|
59
|
-
StackProf.start(mode: :wall, interval: ScoutApm::Agent.instance.config.value("stackprof_interval"))
|
60
|
-
|
61
45
|
begin
|
62
46
|
super
|
63
47
|
rescue Exception
|
64
|
-
ScoutApm::Agent.instance.store.track!("Errors/Request",
|
48
|
+
ScoutApm::Agent.instance.store.track!("Errors/Request",1, :scope => nil)
|
65
49
|
raise
|
66
50
|
ensure
|
67
51
|
Thread::current[:scout_apm_scope_name] = nil
|
68
|
-
StackProf.stop
|
69
|
-
Thread::current[:scout_apm_prof] = StackProf.results
|
70
52
|
end
|
71
53
|
end
|
72
54
|
end
|
@@ -74,3 +56,9 @@ module ScoutApm
|
|
74
56
|
end
|
75
57
|
end
|
76
58
|
|
59
|
+
|
60
|
+
# Rails 3/4
|
61
|
+
module ScoutApm
|
62
|
+
module Instruments
|
63
|
+
end
|
64
|
+
end
|
data/lib/scout_apm/reporter.rb
CHANGED
@@ -16,8 +16,8 @@ module ScoutApm
|
|
16
16
|
end
|
17
17
|
|
18
18
|
# TODO: Parse & return a real response object, not the HTTP Response object
|
19
|
-
def report(payload
|
20
|
-
post(uri, payload
|
19
|
+
def report(payload)
|
20
|
+
post(uri, payload)
|
21
21
|
end
|
22
22
|
|
23
23
|
def uri
|
@@ -26,26 +26,9 @@ module ScoutApm
|
|
26
26
|
URI.parse("#{config.value('host')}/apps/checkin.scout?key=#{config.value('key')}&name=#{CGI.escape(Environment.instance.application_name)}")
|
27
27
|
when :app_server_load
|
28
28
|
URI.parse("#{config.value('host')}/apps/app_server_load.scout?key=#{config.value('key')}&name=#{CGI.escape(Environment.instance.application_name)}")
|
29
|
-
when :deploy_hook
|
30
|
-
URI.parse("#{config.value('host')}/apps/deploy.scout?key=#{config.value('key')}&name=#{CGI.escape(config.value('name'))}")
|
31
29
|
end.tap{|u| logger.debug("Posting to #{u.to_s}")}
|
32
30
|
end
|
33
31
|
|
34
|
-
def can_report?
|
35
|
-
case type
|
36
|
-
when :deploy_hook
|
37
|
-
%w(host key name).each do |k|
|
38
|
-
if config.value(k).nil?
|
39
|
-
logger.warn "/#{type} FAILED: missing required config value for #{k}"
|
40
|
-
return false
|
41
|
-
end
|
42
|
-
end
|
43
|
-
return true
|
44
|
-
else
|
45
|
-
return true
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
32
|
private
|
50
33
|
|
51
34
|
def post(uri, body, headers = Hash.new)
|
@@ -69,8 +52,6 @@ module ScoutApm
|
|
69
52
|
logger.debug "/#{type} OK"
|
70
53
|
when Net::HTTPBadRequest
|
71
54
|
logger.warn "/#{type} FAILED: The Account Key [#{config.value('key')}] is invalid."
|
72
|
-
when Net::HTTPUnprocessableEntity
|
73
|
-
logger.warn "/#{type} FAILED: #{response.body}"
|
74
55
|
else
|
75
56
|
logger.debug "/#{type} FAILED: #{response.inspect}"
|
76
57
|
end
|
@@ -1,49 +1,36 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
class ScoutApm::SlowTransaction
|
2
|
+
BACKTRACE_THRESHOLD = 0.5 # the minimum threshold to record the backtrace for a metric.
|
3
|
+
BACKTRACE_LIMIT = 5 # Max length of callers to display
|
4
|
+
MAX_SIZE = 100 # Limits the size of the metric hash to prevent a metric explosion.
|
5
|
+
attr_reader :metric_name, :total_call_time, :metrics, :meta, :uri, :context, :time, :prof, :raw_prof
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# Given a call stack, generates a filtered backtrace that:
|
18
|
-
# * Limits to the app/models, app/controllers, or app/views directories
|
19
|
-
# * Limits to 5 total callers
|
20
|
-
# * Makes the app folder the top-level folder used in trace info
|
21
|
-
def self.backtrace_parser(backtrace)
|
22
|
-
stack = []
|
23
|
-
backtrace.each do |c|
|
24
|
-
if m=c.match(/(\/app\/(controllers|models|views)\/.+)/)
|
25
|
-
stack << m[1]
|
26
|
-
break if stack.size == BACKTRACE_LIMIT
|
27
|
-
end
|
7
|
+
# Given a call stack, generates a filtered backtrace that:
|
8
|
+
# * Limits to the app/models, app/controllers, or app/views directories
|
9
|
+
# * Limits to 5 total callers
|
10
|
+
# * Makes the app folder the top-level folder used in trace info
|
11
|
+
def self.backtrace_parser(backtrace)
|
12
|
+
stack = []
|
13
|
+
backtrace.each do |c|
|
14
|
+
if m=c.match(/(\/app\/(controllers|models|views)\/.+)/)
|
15
|
+
stack << m[1]
|
16
|
+
break if stack.size == BACKTRACE_LIMIT
|
28
17
|
end
|
29
|
-
stack
|
30
18
|
end
|
19
|
+
stack
|
20
|
+
end
|
31
21
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@raw_prof = raw_stackprof # Send whole data up to server
|
41
|
-
end
|
22
|
+
def initialize(uri,metric_name,total_call_time,metrics,context,time)
|
23
|
+
@uri = uri
|
24
|
+
@metric_name = metric_name
|
25
|
+
@total_call_time = total_call_time
|
26
|
+
@metrics = metrics
|
27
|
+
@context = context
|
28
|
+
@time = time
|
29
|
+
end
|
42
30
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
31
|
+
# Used to remove metrics when the payload will be too large.
|
32
|
+
def clear_metrics!
|
33
|
+
@metrics = nil
|
34
|
+
self
|
48
35
|
end
|
49
36
|
end
|
data/lib/scout_apm/store.rb
CHANGED
@@ -72,7 +72,7 @@ module ScoutApm
|
|
72
72
|
end
|
73
73
|
|
74
74
|
duration = Time.now - item.start_time
|
75
|
-
if last
|
75
|
+
if last=stack.last
|
76
76
|
last.children_time += duration
|
77
77
|
end
|
78
78
|
|
@@ -90,8 +90,8 @@ module ScoutApm
|
|
90
90
|
|
91
91
|
# Uses controllers as the entry point for a transaction. Otherwise, stats are ignored.
|
92
92
|
if stack_empty and meta.metric_name.match(/\AController\//)
|
93
|
-
aggs
|
94
|
-
store_slow(options[:uri],
|
93
|
+
aggs=aggregate_calls(transaction_hash.dup,meta)
|
94
|
+
store_slow(options[:uri],transaction_hash.dup.merge(aggs),meta,stat)
|
95
95
|
# deep duplicate
|
96
96
|
duplicate = aggs.dup
|
97
97
|
duplicate.each_pair do |k,v|
|
@@ -139,20 +139,12 @@ module ScoutApm
|
|
139
139
|
aggregates
|
140
140
|
end
|
141
141
|
|
142
|
-
SLOW_TRANSACTION_THRESHOLD = 2
|
143
|
-
|
144
142
|
# Stores slow transactions. This will be sent to the server.
|
145
|
-
def store_slow(uri,
|
143
|
+
def store_slow(uri,transaction_hash,parent_meta,parent_stat,options = {})
|
146
144
|
@slow_transaction_lock.synchronize do
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
parent_stat.total_call_time,
|
151
|
-
transaction_hash.dup,
|
152
|
-
ScoutApm::Context.current,
|
153
|
-
Thread::current[:scout_apm_trace_time],
|
154
|
-
Thread::current[:scout_apm_prof])
|
155
|
-
@slow_transactions.push(slow_transaction)
|
145
|
+
# tree map of all slow transactions
|
146
|
+
if parent_stat.total_call_time >= 2
|
147
|
+
@slow_transactions.push(ScoutApm::SlowTransaction.new(uri,parent_meta.metric_name,parent_stat.total_call_time,transaction_hash.dup,ScoutApm::Context.current,Thread::current[:scout_apm_trace_time]))
|
156
148
|
ScoutApm::Agent.instance.logger.debug "Slow transaction sample added. [URI: #{uri}] [Context: #{ScoutApm::Context.current.to_hash}] Array Size: #{@slow_transactions.size}"
|
157
149
|
end
|
158
150
|
end
|
data/lib/scout_apm/tracer.rb
CHANGED
@@ -54,16 +54,13 @@ module ScoutApm
|
|
54
54
|
elsif Thread::current[:scout_ignore_children]
|
55
55
|
return yield
|
56
56
|
end
|
57
|
-
|
58
57
|
if options.delete(:scope)
|
59
58
|
Thread::current[:scout_apm_sub_scope] = metric_name
|
60
59
|
end
|
61
|
-
|
62
60
|
if options[:ignore_children]
|
63
61
|
Thread::current[:scout_ignore_children] = true
|
64
62
|
end
|
65
63
|
stack_item = ScoutApm::Agent.instance.store.record(metric_name)
|
66
|
-
|
67
64
|
begin
|
68
65
|
yield
|
69
66
|
ensure
|
@@ -71,21 +68,15 @@ module ScoutApm
|
|
71
68
|
if options[:ignore_children]
|
72
69
|
Thread::current[:scout_ignore_children] = nil
|
73
70
|
end
|
74
|
-
|
75
71
|
ScoutApm::Agent.instance.store.stop_recording(stack_item,options)
|
76
72
|
end
|
77
73
|
end
|
78
74
|
|
79
|
-
def instrument_method(method,
|
75
|
+
def instrument_method(method,options = {})
|
80
76
|
ScoutApm::Agent.instance.logger.info "Instrumenting #{method}"
|
81
77
|
metric_name = options[:metric_name] || default_metric_name(method)
|
82
78
|
return if !instrumentable?(method) or instrumented?(method,metric_name)
|
83
|
-
|
84
|
-
class_eval(instrumented_method_string(
|
85
|
-
method,
|
86
|
-
{:metric_name => metric_name, :scope => options[:scope] }),
|
87
|
-
__FILE__, __LINE__
|
88
|
-
)
|
79
|
+
class_eval instrumented_method_string(method, {:metric_name => metric_name, :scope => options[:scope]}), __FILE__, __LINE__
|
89
80
|
|
90
81
|
alias_method _uninstrumented_method_name(method, metric_name), method
|
91
82
|
alias_method method, _instrumented_method_name(method, metric_name)
|
@@ -95,15 +86,12 @@ module ScoutApm
|
|
95
86
|
|
96
87
|
def instrumented_method_string(method, options)
|
97
88
|
klass = (self === Module) ? "self" : "self.class"
|
98
|
-
|
89
|
+
"def #{_instrumented_method_name(method, options[:metric_name])}(*args, &block)
|
99
90
|
result = #{klass}.instrument(\"#{options[:metric_name]}\",{:scope => #{options[:scope] || false}}) do
|
100
91
|
#{_uninstrumented_method_name(method, options[:metric_name])}(*args, &block)
|
101
92
|
end
|
102
93
|
result
|
103
94
|
end"
|
104
|
-
|
105
|
-
ScoutApm::Agent.instance.logger.debug "Instrumented Method:\n#{method_str}"
|
106
|
-
method_str
|
107
95
|
end
|
108
96
|
|
109
97
|
# The method must exist to be instrumented.
|
data/lib/scout_apm/version.rb
CHANGED
data/scout_apm.gemspec
CHANGED
@@ -18,8 +18,6 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib","data"]
|
20
20
|
|
21
|
-
s.add_dependency 'stackprof'
|
22
|
-
|
23
21
|
s.add_development_dependency "minitest"
|
24
22
|
s.add_development_dependency "pry"
|
25
23
|
s.add_development_dependency "m"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scout_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derek Haynes
|
@@ -9,22 +9,8 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-09-
|
12
|
+
date: 2015-09-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: stackprof
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
requirements:
|
18
|
-
- - ">="
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: '0'
|
21
|
-
type: :runtime
|
22
|
-
prerelease: false
|
23
|
-
version_requirements: !ruby/object:Gem::Requirement
|
24
|
-
requirements:
|
25
|
-
- - ">="
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version: '0'
|
28
14
|
- !ruby/object:Gem::Dependency
|
29
15
|
name: minitest
|
30
16
|
requirement: !ruby/object:Gem::Requirement
|
@@ -89,10 +75,6 @@ files:
|
|
89
75
|
- lib/scout_apm/capacity.rb
|
90
76
|
- lib/scout_apm/config.rb
|
91
77
|
- lib/scout_apm/context.rb
|
92
|
-
- lib/scout_apm/deploy_integrations/capistrano_2.cap
|
93
|
-
- lib/scout_apm/deploy_integrations/capistrano_2.rb
|
94
|
-
- lib/scout_apm/deploy_integrations/capistrano_3.cap
|
95
|
-
- lib/scout_apm/deploy_integrations/capistrano_3.rb
|
96
78
|
- lib/scout_apm/environment.rb
|
97
79
|
- lib/scout_apm/framework_integrations/rails_2.rb
|
98
80
|
- lib/scout_apm/framework_integrations/rails_3_or_4.rb
|
@@ -113,7 +95,6 @@ files:
|
|
113
95
|
- lib/scout_apm/metric_stats.rb
|
114
96
|
- lib/scout_apm/reporter.rb
|
115
97
|
- lib/scout_apm/serializers/app_server_load_serializer.rb
|
116
|
-
- lib/scout_apm/serializers/deploy_serializer.rb
|
117
98
|
- lib/scout_apm/serializers/directive_serializer.rb
|
118
99
|
- lib/scout_apm/serializers/payload_serializer.rb
|
119
100
|
- lib/scout_apm/server_integrations/null.rb
|
@@ -125,10 +106,8 @@ files:
|
|
125
106
|
- lib/scout_apm/server_integrations/webrick.rb
|
126
107
|
- lib/scout_apm/slow_transaction.rb
|
127
108
|
- lib/scout_apm/stack_item.rb
|
128
|
-
- lib/scout_apm/stackprof_tree_collapser.rb
|
129
109
|
- lib/scout_apm/store.rb
|
130
110
|
- lib/scout_apm/tracer.rb
|
131
|
-
- lib/scout_apm/utils/fake_stack_prof.rb
|
132
111
|
- lib/scout_apm/utils/installed_gems.rb
|
133
112
|
- lib/scout_apm/utils/null_logger.rb
|
134
113
|
- lib/scout_apm/utils/sql_sanitizer.rb
|
@@ -158,9 +137,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
158
137
|
version: '0'
|
159
138
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
160
139
|
requirements:
|
161
|
-
- - "
|
140
|
+
- - ">="
|
162
141
|
- !ruby/object:Gem::Version
|
163
|
-
version:
|
142
|
+
version: '0'
|
164
143
|
requirements: []
|
165
144
|
rubyforge_project: scout_apm
|
166
145
|
rubygems_version: 2.2.2
|
@@ -1,12 +0,0 @@
|
|
1
|
-
namespace :scout_apm do
|
2
|
-
namespace :deploy do
|
3
|
-
task :starting do
|
4
|
-
# Warn if missing scout apm deploy creds?
|
5
|
-
end
|
6
|
-
task :finished do
|
7
|
-
ScoutApm::Agent.instance.deploy_integration.report
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
after 'deploy:finished', 'scout_apm:deploy:finished'
|
@@ -1,83 +0,0 @@
|
|
1
|
-
require 'scout_apm'
|
2
|
-
|
3
|
-
module ScoutApm
|
4
|
-
module DeployIntegrations
|
5
|
-
class Capistrano2
|
6
|
-
attr_reader :logger
|
7
|
-
|
8
|
-
def initialize(logger)
|
9
|
-
@logger = logger
|
10
|
-
@cap = defined?(Capistrano::Configuration) ? ObjectSpace.each_object(Capistrano::Configuration).map.first : nil rescue nil
|
11
|
-
end
|
12
|
-
|
13
|
-
def name
|
14
|
-
:capistrano_2
|
15
|
-
end
|
16
|
-
|
17
|
-
def version
|
18
|
-
present? ? Capistrano::VERSION : nil
|
19
|
-
end
|
20
|
-
|
21
|
-
def present?
|
22
|
-
if !@cap.nil? && @cap.is_a?(Capistrano::Configuration)
|
23
|
-
require 'capistrano/version'
|
24
|
-
defined?(Capistrano::VERSION) && Gem::Dependency.new('', '~> 2.0').match?('', Capistrano::VERSION.to_s)
|
25
|
-
else
|
26
|
-
return false
|
27
|
-
end
|
28
|
-
return true
|
29
|
-
rescue
|
30
|
-
return false
|
31
|
-
end
|
32
|
-
|
33
|
-
def install
|
34
|
-
logger.debug "Initializing Capistrano2 Deploy Integration."
|
35
|
-
@cap.load File.expand_path("../capistrano_2.cap", __FILE__)
|
36
|
-
end
|
37
|
-
|
38
|
-
def root
|
39
|
-
'.'
|
40
|
-
end
|
41
|
-
|
42
|
-
def env
|
43
|
-
@cap.fetch(:stage)
|
44
|
-
end
|
45
|
-
|
46
|
-
def found?
|
47
|
-
true
|
48
|
-
end
|
49
|
-
|
50
|
-
def report
|
51
|
-
if reporter.can_report?
|
52
|
-
data = deploy_data
|
53
|
-
logger.debug "Sending deploy hook data: #{data}"
|
54
|
-
payload = ScoutApm::Serializers::DeploySerializer.serialize(data)
|
55
|
-
reporter.report(payload, ScoutApm::Serializers::DeploySerializer::HTTP_HEADERS)
|
56
|
-
else
|
57
|
-
logger.warn "Unable to post deploy hook data"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def reporter
|
62
|
-
@reporter ||= ScoutApm::Reporter.new(:deploy_hook, ScoutApm::Agent.instance.config, @logger)
|
63
|
-
end
|
64
|
-
|
65
|
-
def deploy_data
|
66
|
-
{:revision => current_revision, :branch => branch, :deployed_by => deployed_by}
|
67
|
-
end
|
68
|
-
|
69
|
-
def branch
|
70
|
-
@cap.fetch(:branch)
|
71
|
-
end
|
72
|
-
|
73
|
-
def current_revision
|
74
|
-
@cap.fetch(:current_revision) || `git rev-list --max-count=1 --abbrev-commit --abbrev=12 #{branch}`.chomp
|
75
|
-
end
|
76
|
-
|
77
|
-
def deployed_by
|
78
|
-
ScoutApm::Agent.instance.config.value('deployed_by')
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
namespace :scout_apm do
|
2
|
-
namespace :deploy do
|
3
|
-
task :starting do
|
4
|
-
# Warn if missing scout apm deploy creds?
|
5
|
-
end
|
6
|
-
task :finished do
|
7
|
-
ScoutApm::Agent.instance.deploy_integration.report
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
after 'deploy:finished', 'scout_apm:deploy:finished'
|
@@ -1,82 +0,0 @@
|
|
1
|
-
require 'scout_apm'
|
2
|
-
|
3
|
-
module ScoutApm
|
4
|
-
module DeployIntegrations
|
5
|
-
class Capistrano3
|
6
|
-
attr_reader :logger
|
7
|
-
|
8
|
-
def initialize(logger)
|
9
|
-
@logger = logger
|
10
|
-
@cap = Rake.application rescue nil
|
11
|
-
end
|
12
|
-
|
13
|
-
def name
|
14
|
-
:capistrano_3
|
15
|
-
end
|
16
|
-
|
17
|
-
def version
|
18
|
-
present? ? Capistrano::VERSION : nil
|
19
|
-
end
|
20
|
-
|
21
|
-
def present?
|
22
|
-
if !@cap.nil? && @cap.is_a?(Capistrano::Application)
|
23
|
-
require 'capistrano/version'
|
24
|
-
defined?(Capistrano::VERSION) && Gem::Dependency.new('', '~> 3.0').match?('', Capistrano::VERSION.to_s)
|
25
|
-
else
|
26
|
-
return false
|
27
|
-
end
|
28
|
-
rescue
|
29
|
-
return false
|
30
|
-
end
|
31
|
-
|
32
|
-
def install
|
33
|
-
logger.debug "Initializing Capistrano3 Deploy Integration."
|
34
|
-
load File.expand_path("../capistrano_3.cap", __FILE__)
|
35
|
-
end
|
36
|
-
|
37
|
-
def root
|
38
|
-
'.'
|
39
|
-
end
|
40
|
-
|
41
|
-
def env
|
42
|
-
@cap.fetch(:stage).to_s
|
43
|
-
end
|
44
|
-
|
45
|
-
def found?
|
46
|
-
true
|
47
|
-
end
|
48
|
-
|
49
|
-
def report
|
50
|
-
if reporter.can_report?
|
51
|
-
data = deploy_data
|
52
|
-
logger.debug "Sending deploy hook data: #{data}"
|
53
|
-
payload = ScoutApm::Serializers::DeploySerializer.serialize(data)
|
54
|
-
reporter.report(payload, ScoutApm::Serializers::DeploySerializer::HTTP_HEADERS)
|
55
|
-
else
|
56
|
-
logger.warn "Unable to post deploy hook data"
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def reporter
|
61
|
-
@reporter ||= ScoutApm::Reporter.new(:deploy_hook, ScoutApm::Agent.instance.config, @logger)
|
62
|
-
end
|
63
|
-
|
64
|
-
def deploy_data
|
65
|
-
{:revision => current_revision, :branch => branch, :deployed_by => deployed_by}
|
66
|
-
end
|
67
|
-
|
68
|
-
def branch
|
69
|
-
@cap.fetch(:branch)
|
70
|
-
end
|
71
|
-
|
72
|
-
def current_revision
|
73
|
-
@cap.fetch(:current_revision) || `git rev-list --max-count=1 --abbrev-commit --abbrev=12 #{branch}`.chomp
|
74
|
-
end
|
75
|
-
|
76
|
-
def deployed_by
|
77
|
-
ScoutApm::Agent.instance.config.value('deployed_by')
|
78
|
-
end
|
79
|
-
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# Serialize & deserialize deploy data up to the APM server
|
2
|
-
module ScoutApm
|
3
|
-
module Serializers
|
4
|
-
class DeploySerializer
|
5
|
-
HTTP_HEADERS = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
6
|
-
|
7
|
-
def self.serialize(data)
|
8
|
-
URI.encode_www_form(data)
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.deserialize(data)
|
12
|
-
Marshal.load(data)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,218 +0,0 @@
|
|
1
|
-
# require 'json'; p = JSON::parse(File.read("/Users/cschneid/example_stackprof.out")); p=p.with_indifferent_access; ScoutApm::StackprofTreeCollapser.new(p).call
|
2
|
-
# require 'json'; p = JSON::parse(File.read("/Users/cschneid/profile_appscontroller.json")); p=p.with_indifferent_access; ScoutApm::StackprofTreeCollapser.new(p).call
|
3
|
-
# require 'json'; p = JSON::parse(File.read("/Users/cschneid/profile_elasticsearch.json")); p=p.with_indifferent_access; ScoutApm::StackprofTreeCollapser.new(p).call
|
4
|
-
|
5
|
-
# in_app_nodes.map{|n| [n.samples_for_self_and_descendants, n.name, n.file, n.line]}.sort_by {|x| x[0] }
|
6
|
-
# in_app_nodes.map{|n| [n.total_samples, n.name] }.sort_by {|x| x[0] }
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
module ScoutApm
|
11
|
-
class StackprofTreeCollapser
|
12
|
-
attr_reader :raw_stackprof
|
13
|
-
attr_reader :nodes # the current set of nodes under consideration
|
14
|
-
|
15
|
-
def initialize(raw_stackprof)
|
16
|
-
@raw_stackprof = raw_stackprof
|
17
|
-
ScoutApm::Agent.instance.logger.info("StackProf - Samples: #{raw_stackprof[:samples]}, GC: #{raw_stackprof[:gc_samples]}, missed: #{raw_stackprof[:missed_samples]}, Interval: #{raw_stackprof[:interval]}")
|
18
|
-
end
|
19
|
-
|
20
|
-
def call
|
21
|
-
build_tree
|
22
|
-
connect_children
|
23
|
-
total_samples_of_app_nodes
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def build_tree
|
29
|
-
@nodes = raw_stackprof[:frames].map do |(frame_id, frame_data)|
|
30
|
-
TreeNode.new(frame_id, # frame_id
|
31
|
-
frame_data[:name], # name
|
32
|
-
frame_data[:file], # file
|
33
|
-
frame_data[:line], # line
|
34
|
-
frame_data[:samples], # samples
|
35
|
-
frame_data[:total_samples], # total_samples
|
36
|
-
(frame_data[:edges] || {}), # children_edges [ { id => weight } ]
|
37
|
-
[], # children [ treenode, ... ]
|
38
|
-
[] # parents [ [treenode, int (weight) ], [...] ]
|
39
|
-
)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def connect_children
|
44
|
-
nodes.each do |node|
|
45
|
-
children = nodes.find_all { |n| node.children_edges.keys.include? n.frame_id }
|
46
|
-
|
47
|
-
node.children_edges.each do |(frame_id, weight)|
|
48
|
-
child = children.detect{ |c| c.frame_id == frame_id }
|
49
|
-
child.parents << [node, weight]
|
50
|
-
end
|
51
|
-
|
52
|
-
node.children = children
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def in_app_nodes
|
57
|
-
nodes.select {|n| n.app? }
|
58
|
-
end
|
59
|
-
|
60
|
-
def total_samples_of_app_nodes
|
61
|
-
in_app_nodes.reject{|n| n.calls_only_app_nodes? && !n.has_samples? }.
|
62
|
-
map{|n| { samples: n.total_samples,
|
63
|
-
name: n.name,
|
64
|
-
file: n.file,
|
65
|
-
line: n.line
|
66
|
-
}
|
67
|
-
}
|
68
|
-
end
|
69
|
-
|
70
|
-
# @results will be [{name, samples, file, line}]
|
71
|
-
# def calculate_results
|
72
|
-
# @results = in_app_nodes.map do |node|
|
73
|
-
# desc = node.all_descendants
|
74
|
-
# total_samples = desc.map(&:samples).sum
|
75
|
-
# { desc_count: desc.length, name: node.name, file: node.file, line: node.line, samples: total_samples }
|
76
|
-
# end
|
77
|
-
# end
|
78
|
-
|
79
|
-
# def collapse_tree
|
80
|
-
# while true
|
81
|
-
# number_changed = collapse_tree_one_level
|
82
|
-
# break if number_changed == 0
|
83
|
-
# end
|
84
|
-
# end
|
85
|
-
#
|
86
|
-
# # For each leaf node, sees if it is internal to the monitored app. If not,
|
87
|
-
# # collapse that node to its parents, weighted by the edge counts
|
88
|
-
# # If that node was internal to the monitored app, leave it.
|
89
|
-
# # Returns 0 if nothing changed, a positive integer if things did change,
|
90
|
-
# # indicating how many leaves were collapsed
|
91
|
-
# def collapse_tree_one_level
|
92
|
-
# number_changed = 0
|
93
|
-
#
|
94
|
-
# puts "===========ITERATION==========="
|
95
|
-
# leaves.each do |leaf_node|
|
96
|
-
# next if leaf_node.app?
|
97
|
-
# puts "Collapsing - #{leaf_node.name}"
|
98
|
-
# # app parent: #{leaf_node.self_or_parents_in_app?.map {|x| x.name}}"
|
99
|
-
# number_changed += 1
|
100
|
-
# leaf_node.collapse_to_parent!
|
101
|
-
# @nodes = @nodes.reject { |n| n == leaf_node }
|
102
|
-
# end
|
103
|
-
#
|
104
|
-
# number_changed
|
105
|
-
# end
|
106
|
-
#
|
107
|
-
# # Returns the final result, an array of hashes
|
108
|
-
# def generate_output
|
109
|
-
# leaves.map{|x| { name: x.name, samples: x.samples, file: x.file, line: x.line } }
|
110
|
-
# end
|
111
|
-
#
|
112
|
-
# # A leaf node has no children.
|
113
|
-
# def leaves
|
114
|
-
# nodes.find_all { |n| n.children.empty? }
|
115
|
-
# end
|
116
|
-
#
|
117
|
-
|
118
|
-
###########################################
|
119
|
-
# TreeNode class represents a single node.
|
120
|
-
###########################################
|
121
|
-
TreeNode = Struct.new(:frame_id, :name, :file, :line, :samples, :total_samples,
|
122
|
-
:children_edges, :children, :parents) do
|
123
|
-
def app?
|
124
|
-
@is_app ||= file =~ /^#{ScoutApm::Environment.instance.root}/
|
125
|
-
end
|
126
|
-
|
127
|
-
# My samples, and the weighted samples of all of my children
|
128
|
-
#def samples_for_self_and_descendants(seen=Set.new)
|
129
|
-
# viable_children = children.reject(&:app?)
|
130
|
-
# @samples_for_self_and_descendants ||= samples + viable_children.map{ |c_node|
|
131
|
-
# if seen.include? self
|
132
|
-
# puts "I've already seen #{self.name}, bailing"
|
133
|
-
# return samples # we've already been included, we're looping
|
134
|
-
# else
|
135
|
-
# seen << self
|
136
|
-
# c_node.samples_for_parent(self, seen.dup).tap { |val| puts "Child gave me #{val}" }
|
137
|
-
# end
|
138
|
-
# }.sum
|
139
|
-
#end
|
140
|
-
|
141
|
-
# For this parent of mine, how many of my samples do they get.
|
142
|
-
# is combo of "how many samples do I have, and what's the relative weight of this parent"
|
143
|
-
#def samples_for_parent(p_node, seen=Set.new)
|
144
|
-
# samples_for_self_and_descendants(seen) * relative_weight_of_parent(p_node)
|
145
|
-
#end
|
146
|
-
|
147
|
-
#def relative_weight_of_parent(p_node)
|
148
|
-
# total = parents.map{|(_, weight)| weight}.sum
|
149
|
-
# p_node_weight = parents.detect(0) {|(this_parent, _)| this_parent == p_node }[1]
|
150
|
-
# p_node_weight.to_f / total.to_f
|
151
|
-
#end
|
152
|
-
|
153
|
-
# Allocate this node's samples to its parents, in relation to the rate at
|
154
|
-
# which each parent called this method. Then clear the child from each of the parents
|
155
|
-
#def collapse_to_parent!
|
156
|
-
# total_weight = parents.map{ |(_, weight)| weight }.inject(0){ |sum, weight| sum + weight }
|
157
|
-
# parents.each do |(p_node, weight)|
|
158
|
-
# relative_weight = weight.to_f / total_weight.to_f
|
159
|
-
# p_node.samples += (samples * relative_weight)
|
160
|
-
# end
|
161
|
-
|
162
|
-
# parents.each {|(p_node, _)| p_node.delete_child!(self) }
|
163
|
-
#end
|
164
|
-
|
165
|
-
#def delete_child!(node)
|
166
|
-
# self.children = self.children.reject {|c| c == node }
|
167
|
-
#end
|
168
|
-
|
169
|
-
# Force object_id to be the equality mechanism, rather than struct's
|
170
|
-
# default which delegates to == on each value. That is wrong because
|
171
|
-
# we want to be able to dup a node in the tree construction process and
|
172
|
-
# not have those compare equal to each other.
|
173
|
-
def ==(other)
|
174
|
-
object_id == other.object_id
|
175
|
-
end
|
176
|
-
|
177
|
-
def inspect
|
178
|
-
"#{frame_id}: #{name} - ##{samples}\n" +
|
179
|
-
" Parents: #{parents.map{ |(p, w)| "#{p.name}: #{w}"}.join("\n ") }\n" +
|
180
|
-
" Children: #{children_edges.inspect} \n"
|
181
|
-
end
|
182
|
-
|
183
|
-
#def all_descendants(max_depth=100)
|
184
|
-
# descendants = [self]
|
185
|
-
# unchecked_edge = self.children.reject(&:app?)
|
186
|
-
# stop = false
|
187
|
-
|
188
|
-
# puts "----------------------------------------"
|
189
|
-
|
190
|
-
# while max_depth > 0 && !stop
|
191
|
-
# before_count = descendants.length
|
192
|
-
|
193
|
-
# descendants = (descendants + unchecked_edge).uniq
|
194
|
-
# unchecked_edge = unchecked_edge.map(&:children).flatten.uniq.reject(&:app?)
|
195
|
-
# puts "UncheckedEdge Children: #{unchecked_edge.length}"
|
196
|
-
|
197
|
-
# after_count = descendants.length
|
198
|
-
# stop = true if before_count == after_count
|
199
|
-
# max_depth = max_depth - 1
|
200
|
-
# end
|
201
|
-
|
202
|
-
# puts "#{name} - Found #{descendants.length} children after #{100 - max_depth} iterations"
|
203
|
-
|
204
|
-
# puts "----------------------------------------"
|
205
|
-
|
206
|
-
# descendants
|
207
|
-
#end
|
208
|
-
|
209
|
-
def calls_only_app_nodes?
|
210
|
-
children.all?(&:app?)
|
211
|
-
end
|
212
|
-
|
213
|
-
def has_samples?
|
214
|
-
samples > 0
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# A fake implementation of stackprof, for systems that don't support it.
|
2
|
-
class StackProf
|
3
|
-
def self.start(*args)
|
4
|
-
@running = true
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.stop(*args)
|
8
|
-
@running = false
|
9
|
-
end
|
10
|
-
|
11
|
-
def running?
|
12
|
-
!!@running
|
13
|
-
end
|
14
|
-
|
15
|
-
def run(*args)
|
16
|
-
start
|
17
|
-
yield
|
18
|
-
stop
|
19
|
-
results
|
20
|
-
end
|
21
|
-
|
22
|
-
def sample(*args)
|
23
|
-
end
|
24
|
-
|
25
|
-
def results(*args)
|
26
|
-
{
|
27
|
-
:version => 0.0,
|
28
|
-
:mode => :wall,
|
29
|
-
:interval => 1000,
|
30
|
-
:samples => 0,
|
31
|
-
:gc_samples => 0,
|
32
|
-
:missed_samples => 0,
|
33
|
-
:frames => {},
|
34
|
-
}
|
35
|
-
end
|
36
|
-
end
|