newrelic_rpm 3.9.4.245 → 3.9.5.251
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +57 -0
- data/Guardfile +1 -0
- data/lib/new_relic/agent/agent.rb +3 -3
- data/lib/new_relic/agent/audit_logger.rb +5 -2
- data/lib/new_relic/agent/configuration/default_source.rb +11 -5
- data/lib/new_relic/agent/error_collector.rb +14 -1
- data/lib/new_relic/agent/hostname.rb +22 -1
- data/lib/new_relic/agent/instrumentation/middleware_tracing.rb +8 -2
- data/lib/new_relic/agent/instrumentation/queue_time.rb +9 -6
- data/lib/new_relic/agent/method_tracer.rb +51 -172
- data/lib/new_relic/agent/method_tracer_helpers.rb +90 -0
- data/lib/new_relic/agent/new_relic_service.rb +33 -11
- data/lib/new_relic/agent/new_relic_service/encoders.rb +9 -5
- data/lib/new_relic/agent/request_sampler.rb +20 -12
- data/lib/new_relic/agent/rules_engine.rb +31 -78
- data/lib/new_relic/agent/rules_engine/replacement_rule.rb +76 -0
- data/lib/new_relic/agent/rules_engine/segment_terms_rule.rb +48 -0
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +1 -1
- data/lib/new_relic/agent/sql_sampler.rb +39 -10
- data/lib/new_relic/agent/stats_engine/metric_stats.rb +1 -0
- data/lib/new_relic/agent/stats_engine/stats_hash.rb +7 -13
- data/lib/new_relic/agent/system_info.rb +96 -10
- data/lib/new_relic/agent/threading/agent_thread.rb +4 -1
- data/lib/new_relic/agent/threading/backtrace_node.rb +67 -57
- data/lib/new_relic/agent/threading/thread_profile.rb +30 -15
- data/lib/new_relic/agent/transaction.rb +11 -4
- data/lib/new_relic/environment_report.rb +21 -20
- data/lib/new_relic/version.rb +1 -1
- data/test/agent_helper.rb +12 -0
- data/test/fixtures/cross_agent_tests/README.md +1 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/1pack_1core_1logical.txt +3 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/1pack_1core_2logical.txt +14 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/1pack_2core_2logical.txt +14 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/1pack_4core_4logical.txt +28 -0
- data/test/fixtures/{proc_cpuinfo.txt → cross_agent_tests/proc_cpuinfo/2pack_12core_24logical.txt} +0 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/2pack_20core_40logical.txt +999 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/2pack_2core_2logical.txt +51 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/2pack_2core_4logical.txt +28 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/2pack_4core_4logical.txt +28 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/4pack_4core_4logical.txt +103 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/8pack_8core_8logical.txt +199 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/README.md +24 -0
- data/test/fixtures/cross_agent_tests/proc_cpuinfo/Xpack_Xcore_2logical.txt +43 -0
- data/test/fixtures/cross_agent_tests/transaction_segment_terms.json +101 -0
- data/test/multiverse/lib/multiverse/suite.rb +1 -1
- data/test/multiverse/suites/agent_only/agent_run_id_handling_test.rb +40 -0
- data/test/multiverse/suites/agent_only/labels_test.rb +9 -14
- data/test/multiverse/suites/agent_only/marshaling_test.rb +4 -6
- data/test/multiverse/suites/agent_only/rename_rule_test.rb +41 -4
- data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +11 -3
- data/test/multiverse/suites/config_file_loading/config_file_loading_test.rb +8 -8
- data/test/multiverse/suites/rack/example_app.rb +20 -0
- data/test/multiverse/suites/rack/http_response_code_test.rb +51 -0
- data/test/multiverse/suites/sidekiq/Envfile +13 -6
- data/test/multiverse/suites/sidekiq/sidekiq_server.rb +4 -3
- data/test/new_relic/agent/audit_logger_test.rb +27 -0
- data/test/new_relic/agent/error_collector_test.rb +26 -5
- data/test/new_relic/agent/hostname_test.rb +66 -14
- data/test/new_relic/agent/instrumentation/action_controller_subscriber_test.rb +8 -12
- data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +7 -45
- data/test/new_relic/agent/method_tracer_test.rb +52 -1
- data/test/new_relic/agent/new_relic_service_test.rb +76 -0
- data/test/new_relic/agent/request_sampler_test.rb +7 -0
- data/test/new_relic/agent/rules_engine_test.rb +87 -56
- data/test/new_relic/agent/sql_sampler_test.rb +50 -14
- data/test/new_relic/agent/stats_engine/metric_stats_test.rb +2 -2
- data/test/new_relic/agent/stats_engine/samplers_test.rb +1 -1
- data/test/new_relic/agent/{stats_hash_test.rb → stats_engine/stats_hash_test.rb} +1 -38
- data/test/new_relic/agent/system_info_test.rb +45 -0
- data/test/new_relic/agent/threading/agent_thread_test.rb +30 -0
- data/test/new_relic/agent/threading/backtrace_node_test.rb +27 -44
- data/test/new_relic/agent/threading/thread_profile_test.rb +35 -14
- data/test/new_relic/agent/transaction_test.rb +13 -10
- data/test/new_relic/environment_report_test.rb +7 -6
- data/test/new_relic/fake_collector.rb +10 -6
- data/test/new_relic/multiverse_helpers.rb +4 -11
- data/test/new_relic/rack/agent_hooks_test.rb +1 -1
- data/test/performance/lib/performance/baseline_compare_reporter.rb +24 -7
- data/test/performance/lib/performance/result.rb +3 -1
- data/test/performance/lib/performance/runner.rb +10 -0
- data/test/performance/lib/performance/timer.rb +6 -10
- data/test/performance/script/runner +18 -1
- data/test/performance/suites/queue_time.rb +21 -0
- data/test/performance/suites/stats_hash.rb +34 -0
- data/test/performance/suites/thread_profiling.rb +26 -0
- metadata +25 -4
- metadata.gz.sig +0 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
module NewRelic
|
6
|
+
module Agent
|
7
|
+
class RulesEngine
|
8
|
+
class SegmentTermsRule
|
9
|
+
SEGMENT_PLACEHOLDER = '*'.freeze
|
10
|
+
ADJACENT_PLACEHOLDERS_REGEX = %r{((?:^|/)\*)(?:/\*)*}.freeze
|
11
|
+
ADJACENT_PLACEHOLDERS_REPLACEMENT = '\1'.freeze
|
12
|
+
|
13
|
+
attr_reader :prefix, :terms
|
14
|
+
|
15
|
+
def initialize(options)
|
16
|
+
@prefix = options['prefix']
|
17
|
+
@terms = options['terms']
|
18
|
+
@trim_range = (@prefix.size..-1)
|
19
|
+
end
|
20
|
+
|
21
|
+
def terminal?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def matches?(string)
|
26
|
+
string.start_with?(@prefix)
|
27
|
+
end
|
28
|
+
|
29
|
+
def apply(string)
|
30
|
+
rest = string[@trim_range]
|
31
|
+
leading_slash = rest.slice!(LEADING_SLASH_REGEX)
|
32
|
+
|
33
|
+
segments = rest.split(SEGMENT_SEPARATOR)
|
34
|
+
segments.map! { |s| @terms.include?(s) ? s : SEGMENT_PLACEHOLDER }
|
35
|
+
transformed_suffix = collapse_adjacent_placeholder_segments(segments)
|
36
|
+
|
37
|
+
"#{@prefix}#{leading_slash}#{transformed_suffix}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def collapse_adjacent_placeholder_segments(segments)
|
41
|
+
joined = segments.join(SEGMENT_SEPARATOR)
|
42
|
+
joined.gsub!(ADJACENT_PLACEHOLDERS_REGEX, ADJACENT_PLACEHOLDERS_REPLACEMENT)
|
43
|
+
joined
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -13,7 +13,7 @@ module NewRelic
|
|
13
13
|
named :cpu
|
14
14
|
|
15
15
|
def initialize
|
16
|
-
@processor_count = NewRelic::Agent::SystemInfo.
|
16
|
+
@processor_count = NewRelic::Agent::SystemInfo.num_logical_processors
|
17
17
|
if @processor_count.nil?
|
18
18
|
NewRelic::Agent.logger.warn("Failed to determine processor count, assuming 1")
|
19
19
|
@processor_count = 1
|
@@ -39,6 +39,8 @@ module NewRelic
|
|
39
39
|
# this is for unit tests only
|
40
40
|
attr_reader :sql_traces
|
41
41
|
|
42
|
+
MAX_SAMPLES = 10
|
43
|
+
|
42
44
|
def initialize
|
43
45
|
@sql_traces = {}
|
44
46
|
|
@@ -82,27 +84,55 @@ module NewRelic
|
|
82
84
|
data.set_transaction_name(name)
|
83
85
|
if data.sql_data.size > 0
|
84
86
|
@samples_lock.synchronize do
|
85
|
-
::NewRelic::Agent.logger.debug "
|
86
|
-
|
87
|
+
::NewRelic::Agent.logger.debug "Examining #{data.sql_data.size} slow transaction sql statement(s)"
|
88
|
+
save_slow_sql data
|
87
89
|
end
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
91
93
|
# this should always be called under the @samples_lock
|
92
|
-
def
|
94
|
+
def save_slow_sql(transaction_sql_data)
|
95
|
+
path = transaction_sql_data.path
|
96
|
+
uri = transaction_sql_data.uri
|
97
|
+
|
93
98
|
transaction_sql_data.sql_data.each do |sql_item|
|
94
99
|
normalized_sql = sql_item.normalize
|
95
100
|
sql_trace = @sql_traces[normalized_sql]
|
96
101
|
if sql_trace
|
97
|
-
sql_trace.aggregate(sql_item,
|
98
|
-
transaction_sql_data.uri)
|
102
|
+
sql_trace.aggregate(sql_item, path, uri)
|
99
103
|
else
|
100
|
-
|
101
|
-
|
104
|
+
if has_room?
|
105
|
+
sql_trace = SqlTrace.new(normalized_sql, sql_item, path, uri)
|
106
|
+
elsif should_add_trace?(sql_item)
|
107
|
+
remove_shortest_trace
|
108
|
+
sql_trace = SqlTrace.new(normalized_sql, sql_item, path, uri)
|
109
|
+
end
|
110
|
+
|
111
|
+
if sql_trace
|
112
|
+
@sql_traces[normalized_sql] = sql_trace
|
113
|
+
end
|
102
114
|
end
|
103
115
|
end
|
104
116
|
end
|
105
117
|
|
118
|
+
# this should always be called under the @samples_lock
|
119
|
+
def should_add_trace?(sql_item)
|
120
|
+
@sql_traces.any? do |(_, existing_trace)|
|
121
|
+
existing_trace.max_call_time < sql_item.duration
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# this should always be called under the @samples_lock
|
126
|
+
def has_room?
|
127
|
+
@sql_traces.size < MAX_SAMPLES
|
128
|
+
end
|
129
|
+
|
130
|
+
# this should always be called under the @samples_lock
|
131
|
+
def remove_shortest_trace
|
132
|
+
shortest_key, _ = @sql_traces.min_by { |(_, trace)| trace.max_call_time }
|
133
|
+
@sql_traces.delete(shortest_key)
|
134
|
+
end
|
135
|
+
|
106
136
|
# Records an SQL query, potentially creating a new slow SQL trace, or
|
107
137
|
# aggregating the query into an existing slow SQL trace.
|
108
138
|
#
|
@@ -150,12 +180,11 @@ module NewRelic
|
|
150
180
|
def harvest!
|
151
181
|
return [] unless enabled?
|
152
182
|
|
153
|
-
|
183
|
+
slowest = []
|
154
184
|
@samples_lock.synchronize do
|
155
|
-
|
185
|
+
slowest = @sql_traces.values
|
156
186
|
@sql_traces = {}
|
157
187
|
end
|
158
|
-
slowest = result.sort{|a,b| b.max_call_time <=> a.max_call_time}[0,10]
|
159
188
|
slowest.each {|trace| trace.prepare_to_send }
|
160
189
|
slowest
|
161
190
|
end
|
@@ -10,6 +10,11 @@
|
|
10
10
|
# Missing keys will be automatically created as empty NewRelic::Agent::Stats
|
11
11
|
# instances, so use has_key? explicitly to check for key existence.
|
12
12
|
#
|
13
|
+
# Note that instances of this class are intended to be append-only with respect
|
14
|
+
# to new metrics. That is, you should not attempt to *remove* an entry after it
|
15
|
+
# has been added, only update it (create a new instance if you need to start
|
16
|
+
# over with a blank slate).
|
17
|
+
#
|
13
18
|
# This class makes no provisions for safe usage from multiple threads, such
|
14
19
|
# measures should be externally provided.
|
15
20
|
|
@@ -18,6 +23,7 @@ require 'new_relic/agent/internal_agent_error'
|
|
18
23
|
module NewRelic
|
19
24
|
module Agent
|
20
25
|
class StatsHash < ::Hash
|
26
|
+
|
21
27
|
attr_accessor :started_at, :harvested_at
|
22
28
|
|
23
29
|
def initialize(started_at=Time.now)
|
@@ -68,24 +74,12 @@ module NewRelic
|
|
68
74
|
end
|
69
75
|
end
|
70
76
|
|
71
|
-
class StatsMergerError < NewRelic::Agent::InternalAgentError
|
72
|
-
def initialize(key, destination, source, original_exception)
|
73
|
-
super("Failure when merging stats '#{key}'. In Hash: #{destination.inspect_full}. Merging: #{source.inspect_full}. Original exception: #{original_exception.class} #{original_exception.message}")
|
74
|
-
set_backtrace(original_exception.backtrace)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
77
|
def merge!(other)
|
79
78
|
if other.is_a?(StatsHash) && other.started_at < @started_at
|
80
79
|
@started_at = other.started_at
|
81
80
|
end
|
82
81
|
other.each do |key, val|
|
83
|
-
|
84
|
-
merge_or_insert(key, val)
|
85
|
-
rescue => err
|
86
|
-
NewRelic::Agent.instance.error_collector. \
|
87
|
-
notice_agent_error(StatsMergerError.new(key, self.fetch(key, nil), val, err))
|
88
|
-
end
|
82
|
+
merge_or_insert(key, val)
|
89
83
|
end
|
90
84
|
self
|
91
85
|
end
|
@@ -16,20 +16,106 @@ module NewRelic
|
|
16
16
|
RbConfig::CONFIG['target_os']
|
17
17
|
end
|
18
18
|
|
19
|
-
def self.
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
19
|
+
def self.clear_processor_info
|
20
|
+
@processor_info = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.get_processor_info
|
24
|
+
if @processor_info.nil?
|
25
|
+
case ruby_os_identifier
|
26
|
+
|
27
|
+
when /darwin/, /freebsd/
|
28
|
+
@processor_info = {
|
29
|
+
:num_physical_packages => `sysctl -n hw.packages`.to_i,
|
30
|
+
:num_physical_cores => `sysctl -n hw.physicalcpu_max`.to_i,
|
31
|
+
:num_logical_processors => `sysctl -n hw.logicalcpu_max`.to_i
|
32
|
+
}
|
33
|
+
# in case those don't work, try backup values
|
34
|
+
if @processor_info[:num_physical_cores] <= 0
|
35
|
+
@processor_info[:num_physical_cores] = `sysctl -n hw.physicalcpu`.to_i
|
36
|
+
end
|
37
|
+
if @processor_info[:num_logical_processors] <= 0
|
38
|
+
@processor_info[:num_logical_processors] = `sysctl -n hw.logicalcpu`.to_i
|
39
|
+
end
|
40
|
+
if @processor_info[:num_logical_processors] <= 0
|
41
|
+
@processor_info[:num_logical_processors] = `sysctl -n hw.ncpu`.to_i
|
42
|
+
end
|
43
|
+
if @processor_info[:num_logical_processors] <= 0
|
44
|
+
@processor_info[:num_logical_processors] = `sysctl -n hw.availcpu`.to_i
|
45
|
+
end
|
46
|
+
if @processor_info[:num_logical_processors] <= 0
|
47
|
+
@processor_info[:num_logical_processors] = `sysctl -n hw.activecpu`.to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
when /linux/
|
51
|
+
cpuinfo = proc_try_read('/proc/cpuinfo')
|
52
|
+
@processor_info = cpuinfo ? parse_cpuinfo(cpuinfo) : {}
|
53
|
+
end
|
54
|
+
|
55
|
+
# give nils for obviously wrong values
|
56
|
+
@processor_info.keys.each do |key|
|
57
|
+
@processor_info[key] = nil if @processor_info[key] <= 0
|
58
|
+
end
|
28
59
|
end
|
60
|
+
|
61
|
+
@processor_info
|
29
62
|
rescue
|
30
|
-
|
63
|
+
{}
|
31
64
|
end
|
32
65
|
|
66
|
+
def self.parse_cpuinfo(cpuinfo)
|
67
|
+
# Build a hash of the form
|
68
|
+
# { [phys_id, core_id] => num_logical_processors_on_this_core }
|
69
|
+
cores = Hash.new(0)
|
70
|
+
phys_id = core_id = nil
|
71
|
+
|
72
|
+
total_processors = 0
|
73
|
+
|
74
|
+
cpuinfo.split("\n").map(&:strip).each do |line|
|
75
|
+
case line
|
76
|
+
when /^processor\s*:/
|
77
|
+
cores[[phys_id, core_id]] += 1 if phys_id && core_id
|
78
|
+
phys_id = core_id = nil # reset these values
|
79
|
+
total_processors += 1
|
80
|
+
when /^physical id\s*:(.*)/
|
81
|
+
phys_id = $1.strip.to_i
|
82
|
+
when /^core id\s*:(.*)/
|
83
|
+
core_id = $1.strip.to_i
|
84
|
+
end
|
85
|
+
end
|
86
|
+
cores[[phys_id, core_id]] += 1 if phys_id && core_id
|
87
|
+
|
88
|
+
num_physical_packages = cores.keys.map(&:first).uniq.size
|
89
|
+
num_physical_cores = cores.size
|
90
|
+
num_logical_processors = cores.values.reduce(0,:+)
|
91
|
+
|
92
|
+
if num_physical_cores == 0
|
93
|
+
num_logical_processors = total_processors
|
94
|
+
|
95
|
+
if total_processors == 1
|
96
|
+
# Some older, single-core processors might not list ids,
|
97
|
+
# so we'll just mark them all 1.
|
98
|
+
num_physical_packages = 1
|
99
|
+
num_physical_cores = 1
|
100
|
+
else
|
101
|
+
# We have no way of knowing how many packages or cores
|
102
|
+
# we have, even though we know how many processors there are.
|
103
|
+
num_physical_packages = nil
|
104
|
+
num_physical_cores = nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
{
|
109
|
+
:num_physical_packages => num_physical_packages,
|
110
|
+
:num_physical_cores => num_physical_cores,
|
111
|
+
:num_logical_processors => num_logical_processors
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.num_physical_packages ; get_processor_info[:num_physical_packages ] end
|
116
|
+
def self.num_physical_cores ; get_processor_info[:num_physical_cores ] end
|
117
|
+
def self.num_logical_processors; get_processor_info[:num_logical_processors] end
|
118
|
+
|
33
119
|
def self.processor_arch
|
34
120
|
RbConfig::CONFIG['target_cpu']
|
35
121
|
end
|
@@ -13,7 +13,10 @@ module NewRelic
|
|
13
13
|
begin
|
14
14
|
blk.call
|
15
15
|
rescue => e
|
16
|
-
::NewRelic::Agent.logger.
|
16
|
+
::NewRelic::Agent.logger.error("Thread #{label} exited with error", e)
|
17
|
+
rescue Exception => e
|
18
|
+
::NewRelic::Agent.logger.error("Thread #{label} exited with exception. Re-raising in case of interupt.", e)
|
19
|
+
raise
|
17
20
|
ensure
|
18
21
|
::NewRelic::Agent.logger.debug("Exiting New Relic thread: #{label}")
|
19
22
|
end
|
@@ -5,103 +5,113 @@
|
|
5
5
|
module NewRelic
|
6
6
|
module Agent
|
7
7
|
module Threading
|
8
|
+
MAX_THREAD_PROFILE_DEPTH = 500
|
8
9
|
|
9
|
-
class
|
10
|
-
attr_reader :
|
11
|
-
attr_accessor :runnable_count, :depth
|
12
|
-
|
13
|
-
def initialize(line)
|
14
|
-
if line
|
15
|
-
@raw_line = line
|
16
|
-
@root = false
|
17
|
-
else
|
18
|
-
@root = true
|
19
|
-
end
|
10
|
+
class BacktraceBase
|
11
|
+
attr_reader :children
|
20
12
|
|
13
|
+
def initialize
|
21
14
|
@children = []
|
22
|
-
@
|
23
|
-
@depth = 0
|
15
|
+
@depth = 0
|
24
16
|
end
|
25
17
|
|
26
|
-
def
|
27
|
-
@
|
18
|
+
def add_child_unless_present(child)
|
19
|
+
child.depth = @depth + 1
|
20
|
+
@children << child unless @children.include? child
|
28
21
|
end
|
29
22
|
|
30
|
-
def
|
31
|
-
|
23
|
+
def add_child(child)
|
24
|
+
child.depth = @depth + 1
|
25
|
+
@children << child
|
32
26
|
end
|
33
27
|
|
34
|
-
def
|
28
|
+
def find_child(raw_line)
|
35
29
|
@children.find { |child| child.raw_line == raw_line }
|
36
30
|
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
class BacktraceRoot < BacktraceBase
|
35
|
+
attr_reader :flattened
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
super
|
39
|
+
@flattened = []
|
40
|
+
end
|
37
41
|
|
38
42
|
def ==(other)
|
39
|
-
|
40
|
-
(root? && other.root? || @raw_line == other.raw_line) &&
|
41
|
-
(
|
42
|
-
@depth == other.depth &&
|
43
|
-
@runnable_count == other.runnable_count
|
44
|
-
)
|
45
|
-
)
|
43
|
+
true # all roots are at the same depth and have no raw_line
|
46
44
|
end
|
47
45
|
|
48
|
-
def
|
49
|
-
@
|
46
|
+
def as_array
|
47
|
+
@children.map { |c| c.as_array }.compact
|
50
48
|
end
|
51
49
|
|
52
50
|
def aggregate(backtrace)
|
53
51
|
current = self
|
54
52
|
|
53
|
+
depth = 0
|
55
54
|
backtrace.reverse_each do |frame|
|
56
|
-
|
55
|
+
break if depth >= MAX_THREAD_PROFILE_DEPTH
|
56
|
+
|
57
|
+
existing_node = current.find_child(frame)
|
57
58
|
if existing_node
|
58
59
|
node = existing_node
|
59
60
|
else
|
60
61
|
node = Threading::BacktraceNode.new(frame)
|
61
|
-
current.
|
62
|
+
current.add_child(node)
|
63
|
+
@flattened << node
|
62
64
|
end
|
63
65
|
|
64
66
|
node.runnable_count += 1
|
65
67
|
current = node
|
68
|
+
depth += 1
|
66
69
|
end
|
67
70
|
end
|
68
71
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
+
def dump_string
|
73
|
+
result = "#<BacktraceRoot:#{object_id}>"
|
74
|
+
child_results = @children.map { |c| c.dump_string(2) }.join("\n")
|
75
|
+
result << "\n" unless child_results.empty?
|
76
|
+
result << child_results
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
class BacktraceNode < BacktraceBase
|
82
|
+
attr_reader :file, :method, :line_no, :raw_line, :as_array
|
83
|
+
attr_accessor :runnable_count, :depth
|
84
|
+
|
85
|
+
def initialize(line)
|
86
|
+
super()
|
87
|
+
@raw_line = line
|
88
|
+
@children = []
|
89
|
+
@runnable_count = 0
|
90
|
+
end
|
91
|
+
|
92
|
+
def ==(other)
|
93
|
+
(
|
94
|
+
@raw_line == other.raw_line &&
|
95
|
+
@depth == other.depth &&
|
96
|
+
@runnable_count == other.runnable_count
|
97
|
+
)
|
72
98
|
end
|
73
99
|
|
74
|
-
def
|
75
|
-
|
76
|
-
@children.inject(initial) { |all, child| all.concat(child.flatten) }
|
100
|
+
def mark_for_array_conversion
|
101
|
+
@as_array = []
|
77
102
|
end
|
78
103
|
|
79
104
|
include NewRelic::Coerce
|
80
105
|
|
81
|
-
def
|
82
|
-
child_arrays = @children.map { |c| c.
|
83
|
-
return child_arrays if root?
|
84
|
-
file, method, line = parse_backtrace_frame(@raw_line)
|
85
|
-
[
|
86
|
-
[
|
87
|
-
string(file),
|
88
|
-
string(method),
|
89
|
-
int(line)
|
90
|
-
],
|
91
|
-
int(@runnable_count),
|
92
|
-
0,
|
93
|
-
child_arrays
|
94
|
-
]
|
95
|
-
end
|
106
|
+
def complete_array_conversion
|
107
|
+
child_arrays = @children.map { |c| c.as_array }.compact
|
96
108
|
|
97
|
-
|
98
|
-
child.depth = @depth + 1
|
99
|
-
@children << child unless @children.include? child
|
100
|
-
end
|
109
|
+
file, method, line = parse_backtrace_frame(@raw_line)
|
101
110
|
|
102
|
-
|
103
|
-
@
|
104
|
-
@
|
111
|
+
@as_array << [string(file), string(method), int(line)]
|
112
|
+
@as_array << int(@runnable_count)
|
113
|
+
@as_array << 0
|
114
|
+
@as_array << child_arrays
|
105
115
|
end
|
106
116
|
|
107
117
|
def dump_string(indent=0)
|