newrelic_rpm 2.13.4 → 2.13.5.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of newrelic_rpm might be problematic. Click here for more details.
- data/CHANGELOG +9 -0
- data/lib/new_relic/agent.rb +2 -1
- data/lib/new_relic/agent/agent.rb +393 -204
- data/lib/new_relic/agent/error_collector.rb +113 -43
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +14 -16
- data/lib/new_relic/agent/instrumentation/queue_time.rb +201 -0
- data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +1 -1
- data/lib/new_relic/agent/instrumentation/sequel.rb +95 -0
- data/lib/new_relic/agent/method_tracer.rb +391 -313
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +43 -41
- data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +2 -0
- data/lib/new_relic/agent/samplers/memory_sampler.rb +122 -120
- data/lib/new_relic/agent/samplers/object_sampler.rb +2 -0
- data/lib/new_relic/agent/stats_engine/metric_stats.rb +0 -1
- data/lib/new_relic/agent/stats_engine/samplers.rb +20 -14
- data/lib/new_relic/agent/stats_engine/transactions.rb +35 -7
- data/lib/new_relic/control.rb +12 -17
- data/lib/new_relic/control/configuration.rb +1 -0
- data/lib/new_relic/control/frameworks/rails.rb +7 -4
- data/lib/new_relic/control/frameworks/rails3.rb +1 -1
- data/lib/new_relic/control/instrumentation.rb +2 -18
- data/lib/new_relic/local_environment.rb +117 -59
- data/lib/new_relic/rack/developer_mode.rb +212 -207
- data/lib/new_relic/recipes.rb +0 -9
- data/lib/new_relic/stats.rb +87 -81
- data/lib/new_relic/transaction_analysis.rb +1 -1
- data/lib/new_relic/version.rb +2 -2
- data/lib/newrelic_rpm.rb +2 -3
- data/lib/tasks/tests.rake +5 -1
- data/newrelic_rpm.gemspec +14 -5
- data/test/config/test_control.rb +14 -2
- data/test/new_relic/agent/active_record_instrumentation_test.rb +342 -119
- data/test/new_relic/agent/add_method_tracer_test.rb +158 -0
- data/test/new_relic/agent/agent_connect_test.rb +295 -0
- data/test/new_relic/agent/agent_controller_test.rb +86 -18
- data/test/new_relic/agent/agent_start_test.rb +326 -0
- data/test/new_relic/agent/agent_start_worker_thread_test.rb +157 -0
- data/test/new_relic/agent/apdex_from_server_test.rb +9 -0
- data/test/new_relic/agent/collection_helper_test.rb +3 -1
- data/test/new_relic/agent/error_collector_notice_error_test.rb +255 -0
- data/test/new_relic/agent/error_collector_test.rb +6 -0
- data/test/new_relic/agent/method_tracer_test.rb +2 -2
- data/test/new_relic/agent/method_tracer_trace_execution_scoped_test.rb +233 -0
- data/test/new_relic/agent/net_instrumentation_test.rb +17 -12
- data/test/new_relic/agent/queue_time_test.rb +333 -0
- data/test/new_relic/agent/rpm_agent_test.rb +4 -2
- data/test/new_relic/agent/stats_engine/samplers_test.rb +27 -1
- data/test/new_relic/agent/transaction_sample_subtest_test.rb +56 -0
- data/test/new_relic/agent/transaction_sample_test.rb +103 -174
- data/test/new_relic/agent/transaction_sampler_test.rb +9 -2
- data/test/new_relic/control_test.rb +7 -2
- data/test/new_relic/metric_spec_test.rb +1 -1
- data/test/new_relic/stats_test.rb +112 -15
- data/test/test_helper.rb +79 -16
- data/ui/helpers/developer_mode_helper.rb +2 -0
- metadata +19 -7
- data/lib/new_relic_api.rb +0 -276
@@ -1,54 +1,56 @@
|
|
1
|
+
require 'new_relic/agent/sampler'
|
2
|
+
|
1
3
|
module NewRelic
|
2
4
|
module Agent
|
3
5
|
module Samplers
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
class CpuSampler < NewRelic::Agent::Sampler
|
7
|
+
attr_reader :last_time
|
8
|
+
def initialize
|
9
|
+
super :cpu
|
10
|
+
poll
|
11
|
+
end
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
def user_util_stats
|
14
|
+
stats_engine.get_stats_no_scope("CPU/User/Utilization")
|
15
|
+
end
|
16
|
+
def system_util_stats
|
17
|
+
stats_engine.get_stats_no_scope("CPU/System/Utilization")
|
18
|
+
end
|
19
|
+
def usertime_stats
|
20
|
+
stats_engine.get_stats_no_scope("CPU/User Time")
|
21
|
+
end
|
22
|
+
def systemtime_stats
|
23
|
+
stats_engine.get_stats_no_scope("CPU/System Time")
|
24
|
+
end
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
26
|
+
def self.supported_on_this_platform?
|
27
|
+
not defined?(JRuby)
|
28
|
+
end
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
def poll
|
31
|
+
now = Time.now
|
32
|
+
t = Process.times
|
33
|
+
if @last_time
|
34
|
+
elapsed = now - @last_time
|
35
|
+
return if elapsed < 1 # Causing some kind of math underflow
|
36
|
+
num_processors = NewRelic::Control.instance.local_env.processors || 1
|
37
|
+
usertime = t.utime - @last_utime
|
38
|
+
systemtime = t.stime - @last_stime
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
+
systemtime_stats.record_data_point(systemtime) if systemtime >= 0
|
41
|
+
usertime_stats.record_data_point(usertime) if usertime >= 0
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
# Calculate the true utilization by taking cpu times and dividing by
|
44
|
+
# elapsed time X num_processors.
|
45
|
+
user_util_stats.record_data_point usertime / (elapsed * num_processors)
|
46
|
+
system_util_stats.record_data_point systemtime / (elapsed * num_processors)
|
47
|
+
end
|
48
|
+
@last_utime = t.utime
|
49
|
+
@last_stime = t.stime
|
50
|
+
@last_time = now
|
51
|
+
end
|
45
52
|
end
|
46
|
-
@last_utime = t.utime
|
47
|
-
@last_stime = t.stime
|
48
|
-
@last_time = now
|
49
53
|
end
|
50
54
|
end
|
51
55
|
end
|
52
|
-
end
|
53
|
-
end
|
54
56
|
|
@@ -1,142 +1,144 @@
|
|
1
|
+
require 'new_relic/agent/sampler'
|
2
|
+
|
1
3
|
module NewRelic
|
2
4
|
module Agent
|
3
|
-
module Samplers
|
5
|
+
module Samplers
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
+
class MemorySampler < NewRelic::Agent::Sampler
|
8
|
+
attr_accessor :sampler
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
10
|
+
def initialize
|
11
|
+
super :memory
|
12
|
+
# macos, linux, solaris
|
13
|
+
if defined? JRuby
|
14
|
+
@sampler = JavaHeapSampler.new
|
15
|
+
elsif platform =~ /linux/
|
16
|
+
@sampler = ProcStatus.new
|
17
|
+
if !@sampler.can_run?
|
18
|
+
NewRelic::Agent.instance.warn.debug "Error attempting to use /proc/#{$$}/status file for reading memory. Using ps command instead."
|
19
|
+
@sampler = ShellPS.new("ps -o rsz")
|
20
|
+
else
|
21
|
+
NewRelic::Agent.instance.log.debug "Using /proc/#{$$}/status for reading process memory."
|
22
|
+
end
|
23
|
+
elsif platform =~ /darwin9/ # 10.5
|
24
|
+
@sampler = ShellPS.new("ps -o rsz")
|
25
|
+
elsif platform =~ /darwin10/ # 10.6
|
26
|
+
@sampler = ShellPS.new("ps -o rss")
|
27
|
+
elsif platform =~ /freebsd/
|
28
|
+
@sampler = ShellPS.new("ps -o rss")
|
29
|
+
elsif platform =~ /solaris/
|
30
|
+
@sampler = ShellPS.new("/usr/bin/ps -o rss -p")
|
31
|
+
end
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
raise Unsupported, "Unsupported platform for getting memory: #{platform}" if @sampler.nil?
|
34
|
+
raise Unsupported, "Unable to run #{@sampler}" unless @sampler.can_run?
|
35
|
+
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
def self.supported_on_this_platform?
|
38
|
+
defined?(JRuby) or platform =~ /linux|darwin9|darwin10|freebsd|solaris/
|
39
|
+
end
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
41
|
+
def self.platform
|
42
|
+
if RUBY_PLATFORM =~ /java/
|
43
|
+
%x[uname -s].downcase
|
44
|
+
else
|
45
|
+
RUBY_PLATFORM.downcase
|
46
|
+
end
|
47
|
+
end
|
48
|
+
def platform
|
49
|
+
NewRelic::Agent::Samplers::MemorySampler.platform
|
50
|
+
end
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
52
|
+
def stats
|
53
|
+
stats_engine.get_stats("Memory/Physical", false)
|
54
|
+
end
|
55
|
+
def poll
|
56
|
+
sample = @sampler.get_sample
|
57
|
+
stats.record_data_point sample if sample
|
58
|
+
stats
|
59
|
+
end
|
60
|
+
class Base
|
61
|
+
def can_run?
|
62
|
+
return false if @broken
|
63
|
+
m = get_memory rescue nil
|
64
|
+
m && m > 0
|
65
|
+
end
|
66
|
+
def get_sample
|
67
|
+
return nil if @broken
|
68
|
+
begin
|
69
|
+
m = get_memory
|
70
|
+
if m.nil?
|
71
|
+
NewRelic::Agent.instance.log.error "Unable to get the resident memory for process #{$$}. Disabling memory sampler."
|
72
|
+
@broken = true
|
73
|
+
end
|
74
|
+
return m
|
75
|
+
rescue => e
|
76
|
+
NewRelic::Agent.instance.log.error "Unable to get the resident memory for process #{$$}. (#{e})"
|
77
|
+
NewRelic::Agent.instance.log.debug e.backtrace.join("\n ")
|
78
|
+
NewRelic::Agent.instance.log.error "Disabling memory sampler."
|
79
|
+
@broken = true
|
80
|
+
return nil
|
81
|
+
end
|
71
82
|
end
|
72
|
-
return m
|
73
|
-
rescue => e
|
74
|
-
NewRelic::Agent.instance.log.error "Unable to get the resident memory for process #{$$}. (#{e})"
|
75
|
-
NewRelic::Agent.instance.log.debug e.backtrace.join("\n ")
|
76
|
-
NewRelic::Agent.instance.log.error "Disabling memory sampler."
|
77
|
-
@broken = true
|
78
|
-
return nil
|
79
83
|
end
|
80
|
-
end
|
81
|
-
end
|
82
84
|
|
83
|
-
|
85
|
+
class JavaHeapSampler < Base
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
87
|
+
def get_memory
|
88
|
+
raise "Can't sample Java heap unless running in JRuby" unless defined? JRuby
|
89
|
+
java.lang.Runtime.getRuntime.totalMemory / (1024 * 1024).to_f rescue nil
|
90
|
+
end
|
91
|
+
def to_s
|
92
|
+
"JRuby Java heap sampler"
|
93
|
+
end
|
94
|
+
end
|
93
95
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
96
|
+
class ShellPS < Base
|
97
|
+
def initialize(command)
|
98
|
+
super()
|
99
|
+
@command = command
|
100
|
+
end
|
101
|
+
# Returns the amount of resident memory this process is using in MB
|
102
|
+
#
|
103
|
+
def get_memory
|
104
|
+
process = $$
|
105
|
+
memory = `#{@command} #{process}`.split("\n")[1].to_f / 1024.0 rescue nil
|
106
|
+
# if for some reason the ps command doesn't work on the resident os,
|
107
|
+
# then don't execute it any more.
|
108
|
+
raise "Faulty command: `#{@command} #{process}`" if memory.nil? || memory <= 0
|
109
|
+
memory
|
110
|
+
end
|
111
|
+
def to_s
|
112
|
+
"shell command sampler: #{@command}"
|
113
|
+
end
|
114
|
+
end
|
113
115
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
116
|
+
# ProcStatus
|
117
|
+
#
|
118
|
+
# A class that samples memory by reading the file /proc/$$/status, which is specific to linux
|
119
|
+
#
|
120
|
+
class ProcStatus < Base
|
119
121
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
122
|
+
# Returns the amount of resident memory this process is using in MB
|
123
|
+
#
|
124
|
+
def get_memory
|
125
|
+
File.open(proc_status_file, "r") do |f|
|
126
|
+
while !f.eof?
|
127
|
+
if f.readline =~ /RSS:\s*(\d+) kB/i
|
128
|
+
return $1.to_f / 1024.0
|
129
|
+
end
|
130
|
+
end
|
127
131
|
end
|
132
|
+
raise "Unable to find RSS in #{proc_status_file}"
|
133
|
+
end
|
134
|
+
def proc_status_file
|
135
|
+
"/proc/#{$$}/status"
|
136
|
+
end
|
137
|
+
def to_s
|
138
|
+
"proc status file sampler: #{proc_status_file}"
|
128
139
|
end
|
129
140
|
end
|
130
|
-
raise "Unable to find RSS in #{proc_status_file}"
|
131
|
-
end
|
132
|
-
def proc_status_file
|
133
|
-
"/proc/#{$$}/status"
|
134
|
-
end
|
135
|
-
def to_s
|
136
|
-
"proc status file sampler: #{proc_status_file}"
|
137
141
|
end
|
138
142
|
end
|
139
143
|
end
|
140
144
|
end
|
141
|
-
end
|
142
|
-
end
|
@@ -116,7 +116,6 @@ module NewRelic
|
|
116
116
|
# data
|
117
117
|
previous_metric_data = previous_timeslice_data[metric_spec]
|
118
118
|
stats_copy.merge! previous_metric_data.stats unless previous_metric_data.nil?
|
119
|
-
stats_copy.round!
|
120
119
|
|
121
120
|
# don't bother collecting and reporting stats that have zero-values for this timeslice.
|
122
121
|
# significant performance boost and storage savings.
|
@@ -32,25 +32,31 @@ module Agent
|
|
32
32
|
@sampler_thread['newrelic_label'] = 'Sampler Tasks'
|
33
33
|
end
|
34
34
|
|
35
|
+
private
|
36
|
+
|
37
|
+
def add_sampler_to(sampler_array, sampler)
|
38
|
+
raise "Sampler #{sampler.inspect} is already registered. Don't call add_sampler directly anymore." if sampler_array.include?(sampler)
|
39
|
+
sampler_array << sampler
|
40
|
+
sampler.stats_engine = self
|
41
|
+
end
|
42
|
+
|
43
|
+
def log_added_sampler(type, sampler)
|
44
|
+
log.debug "Adding #{type} sampler: #{sampler.inspect}"
|
45
|
+
end
|
46
|
+
|
47
|
+
public
|
48
|
+
|
35
49
|
# Add an instance of Sampler to be invoked about every 10 seconds on a background
|
36
50
|
# thread.
|
37
|
-
def add_sampler
|
38
|
-
periodic_samplers
|
39
|
-
|
40
|
-
end
|
41
|
-
periodic_samplers << sampler
|
42
|
-
sampler.stats_engine = self
|
43
|
-
log.debug "Adding sampler #{sampler.object_id.to_s}"
|
51
|
+
def add_sampler(sampler)
|
52
|
+
add_sampler_to(periodic_samplers, sampler)
|
53
|
+
log_added_sampler('periodic', sampler)
|
44
54
|
end
|
45
55
|
|
46
56
|
# Add a sampler to be invoked just before each harvest.
|
47
|
-
def add_harvest_sampler
|
48
|
-
harvest_samplers
|
49
|
-
|
50
|
-
end
|
51
|
-
harvest_samplers << sampler
|
52
|
-
sampler.stats_engine = self
|
53
|
-
log.debug "Adding harvest time sampler: #{sampler.id.to_s}"
|
57
|
+
def add_harvest_sampler(sampler)
|
58
|
+
add_sampler_to(harvest_samplers, sampler)
|
59
|
+
log_added_sampler('harvest-time', sampler)
|
54
60
|
end
|
55
61
|
|
56
62
|
private
|
@@ -41,8 +41,8 @@ module Agent
|
|
41
41
|
if collecting_gc?
|
42
42
|
if stack.empty?
|
43
43
|
# reset the gc time so we only include gc time spent during this call
|
44
|
-
@last_gc_timestamp =
|
45
|
-
@last_gc_count =
|
44
|
+
@last_gc_timestamp = gc_time
|
45
|
+
@last_gc_count = gc_collections
|
46
46
|
else
|
47
47
|
capture_gc_time
|
48
48
|
end
|
@@ -113,17 +113,45 @@ module Agent
|
|
113
113
|
|
114
114
|
# Make sure we don't do this in a multi-threaded environment
|
115
115
|
def collecting_gc?
|
116
|
-
|
116
|
+
if !defined?(@@collecting_gc)
|
117
|
+
@@collecting_gc = false
|
118
|
+
if !NewRelic::Control.instance.multi_threaded?
|
119
|
+
@@collecting_gc = true if GC.respond_to?(:time) && GC.respond_to?(:collections) # 1.8.x
|
120
|
+
@@collecting_gc = true if defined?(GC::Profiler) && GC::Profiler.enabled? # 1.9.2
|
121
|
+
end
|
122
|
+
end
|
123
|
+
@@collecting_gc
|
124
|
+
end
|
125
|
+
|
126
|
+
# The total number of times the garbage collector has run since
|
127
|
+
# profiling was enabled
|
128
|
+
def gc_collections
|
129
|
+
if GC.respond_to?(:count)
|
130
|
+
GC.count
|
131
|
+
elsif GC.respond_to?(:collections)
|
132
|
+
GC.collections
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# The total amount of time taken by garbage collection since
|
137
|
+
# profiling was enabled
|
138
|
+
def gc_time
|
139
|
+
if GC.respond_to?(:time)
|
140
|
+
GC.time
|
141
|
+
elsif defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time)
|
142
|
+
# The 1.9 profiler returns a time in usec
|
143
|
+
GC::Profiler.total_time * 1000000.0
|
144
|
+
end
|
117
145
|
end
|
118
146
|
|
119
147
|
# Assumes collecting_gc?
|
120
148
|
def capture_gc_time
|
121
149
|
# Skip this if we are already in this segment
|
122
150
|
return if !scope_stack.empty? && scope_stack.last.name == "GC/cumulative"
|
123
|
-
num_calls =
|
124
|
-
elapsed = (
|
125
|
-
@last_gc_timestamp =
|
126
|
-
@last_gc_count =
|
151
|
+
num_calls = gc_collections - @last_gc_count
|
152
|
+
elapsed = (gc_time - @last_gc_timestamp).to_f
|
153
|
+
@last_gc_timestamp = gc_time
|
154
|
+
@last_gc_count = gc_collections
|
127
155
|
if num_calls > 0
|
128
156
|
# µs to seconds
|
129
157
|
elapsed = elapsed / 1000000.0
|