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.

Files changed (57) hide show
  1. data/CHANGELOG +9 -0
  2. data/lib/new_relic/agent.rb +2 -1
  3. data/lib/new_relic/agent/agent.rb +393 -204
  4. data/lib/new_relic/agent/error_collector.rb +113 -43
  5. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +14 -16
  6. data/lib/new_relic/agent/instrumentation/queue_time.rb +201 -0
  7. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +1 -1
  8. data/lib/new_relic/agent/instrumentation/sequel.rb +95 -0
  9. data/lib/new_relic/agent/method_tracer.rb +391 -313
  10. data/lib/new_relic/agent/samplers/cpu_sampler.rb +43 -41
  11. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +2 -0
  12. data/lib/new_relic/agent/samplers/memory_sampler.rb +122 -120
  13. data/lib/new_relic/agent/samplers/object_sampler.rb +2 -0
  14. data/lib/new_relic/agent/stats_engine/metric_stats.rb +0 -1
  15. data/lib/new_relic/agent/stats_engine/samplers.rb +20 -14
  16. data/lib/new_relic/agent/stats_engine/transactions.rb +35 -7
  17. data/lib/new_relic/control.rb +12 -17
  18. data/lib/new_relic/control/configuration.rb +1 -0
  19. data/lib/new_relic/control/frameworks/rails.rb +7 -4
  20. data/lib/new_relic/control/frameworks/rails3.rb +1 -1
  21. data/lib/new_relic/control/instrumentation.rb +2 -18
  22. data/lib/new_relic/local_environment.rb +117 -59
  23. data/lib/new_relic/rack/developer_mode.rb +212 -207
  24. data/lib/new_relic/recipes.rb +0 -9
  25. data/lib/new_relic/stats.rb +87 -81
  26. data/lib/new_relic/transaction_analysis.rb +1 -1
  27. data/lib/new_relic/version.rb +2 -2
  28. data/lib/newrelic_rpm.rb +2 -3
  29. data/lib/tasks/tests.rake +5 -1
  30. data/newrelic_rpm.gemspec +14 -5
  31. data/test/config/test_control.rb +14 -2
  32. data/test/new_relic/agent/active_record_instrumentation_test.rb +342 -119
  33. data/test/new_relic/agent/add_method_tracer_test.rb +158 -0
  34. data/test/new_relic/agent/agent_connect_test.rb +295 -0
  35. data/test/new_relic/agent/agent_controller_test.rb +86 -18
  36. data/test/new_relic/agent/agent_start_test.rb +326 -0
  37. data/test/new_relic/agent/agent_start_worker_thread_test.rb +157 -0
  38. data/test/new_relic/agent/apdex_from_server_test.rb +9 -0
  39. data/test/new_relic/agent/collection_helper_test.rb +3 -1
  40. data/test/new_relic/agent/error_collector_notice_error_test.rb +255 -0
  41. data/test/new_relic/agent/error_collector_test.rb +6 -0
  42. data/test/new_relic/agent/method_tracer_test.rb +2 -2
  43. data/test/new_relic/agent/method_tracer_trace_execution_scoped_test.rb +233 -0
  44. data/test/new_relic/agent/net_instrumentation_test.rb +17 -12
  45. data/test/new_relic/agent/queue_time_test.rb +333 -0
  46. data/test/new_relic/agent/rpm_agent_test.rb +4 -2
  47. data/test/new_relic/agent/stats_engine/samplers_test.rb +27 -1
  48. data/test/new_relic/agent/transaction_sample_subtest_test.rb +56 -0
  49. data/test/new_relic/agent/transaction_sample_test.rb +103 -174
  50. data/test/new_relic/agent/transaction_sampler_test.rb +9 -2
  51. data/test/new_relic/control_test.rb +7 -2
  52. data/test/new_relic/metric_spec_test.rb +1 -1
  53. data/test/new_relic/stats_test.rb +112 -15
  54. data/test/test_helper.rb +79 -16
  55. data/ui/helpers/developer_mode_helper.rb +2 -0
  56. metadata +19 -7
  57. 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
- class CpuSampler < NewRelic::Agent::Sampler
5
- attr_reader :last_time
6
- def initialize
7
- super :cpu
8
- poll
9
- end
6
+ class CpuSampler < NewRelic::Agent::Sampler
7
+ attr_reader :last_time
8
+ def initialize
9
+ super :cpu
10
+ poll
11
+ end
10
12
 
11
- def user_util_stats
12
- stats_engine.get_stats_no_scope("CPU/User/Utilization")
13
- end
14
- def system_util_stats
15
- stats_engine.get_stats_no_scope("CPU/System/Utilization")
16
- end
17
- def usertime_stats
18
- stats_engine.get_stats_no_scope("CPU/User Time")
19
- end
20
- def systemtime_stats
21
- stats_engine.get_stats_no_scope("CPU/System Time")
22
- end
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
- def self.supported_on_this_platform?
25
- not defined?(JRuby)
26
- end
26
+ def self.supported_on_this_platform?
27
+ not defined?(JRuby)
28
+ end
27
29
 
28
- def poll
29
- now = Time.now
30
- t = Process.times
31
- if @last_time
32
- elapsed = now - @last_time
33
- return if elapsed < 1 # Causing some kind of math underflow
34
- num_processors = NewRelic::Control.instance.local_env.processors || 1
35
- usertime = t.utime - @last_utime
36
- systemtime = t.stime - @last_stime
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
- systemtime_stats.record_data_point(systemtime) if systemtime >= 0
39
- usertime_stats.record_data_point(usertime) if usertime >= 0
40
+ systemtime_stats.record_data_point(systemtime) if systemtime >= 0
41
+ usertime_stats.record_data_point(usertime) if usertime >= 0
40
42
 
41
- # Calculate the true utilization by taking cpu times and dividing by
42
- # elapsed time X num_processors.
43
- user_util_stats.record_data_point usertime / (elapsed * num_processors)
44
- system_util_stats.record_data_point systemtime / (elapsed * num_processors)
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,3 +1,5 @@
1
+ require 'new_relic/agent/sampler'
2
+
1
3
  module NewRelic
2
4
  module Agent
3
5
  module Samplers
@@ -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
- class MemorySampler < NewRelic::Agent::Sampler
6
- attr_accessor :sampler
7
+ class MemorySampler < NewRelic::Agent::Sampler
8
+ attr_accessor :sampler
7
9
 
8
- def initialize
9
- super :memory
10
- # macos, linux, solaris
11
- if defined? JRuby
12
- @sampler = JavaHeapSampler.new
13
- elsif platform =~ /linux/
14
- @sampler = ProcStatus.new
15
- if !@sampler.can_run?
16
- NewRelic::Agent.instance.warn.debug "Error attempting to use /proc/#{$$}/status file for reading memory. Using ps command instead."
17
- @sampler = ShellPS.new("ps -o rsz")
18
- else
19
- NewRelic::Agent.instance.log.debug "Using /proc/#{$$}/status for reading process memory."
20
- end
21
- elsif platform =~ /darwin9/ # 10.5
22
- @sampler = ShellPS.new("ps -o rsz")
23
- elsif platform =~ /darwin10/ # 10.6
24
- @sampler = ShellPS.new("ps -o rss")
25
- elsif platform =~ /freebsd/
26
- @sampler = ShellPS.new("ps -o rss")
27
- elsif platform =~ /solaris/
28
- @sampler = ShellPS.new("/usr/bin/ps -o rss -p")
29
- end
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
- raise Unsupported, "Unsupported platform for getting memory: #{platform}" if @sampler.nil?
32
- raise Unsupported, "Unable to run #{@sampler}" unless @sampler.can_run?
33
- end
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
- def self.supported_on_this_platform?
36
- defined?(JRuby) or platform =~ /linux|darwin9|darwin10|freebsd|solaris/
37
- end
37
+ def self.supported_on_this_platform?
38
+ defined?(JRuby) or platform =~ /linux|darwin9|darwin10|freebsd|solaris/
39
+ end
38
40
 
39
- def self.platform
40
- if RUBY_PLATFORM =~ /java/
41
- %x[uname -s].downcase
42
- else
43
- RUBY_PLATFORM.downcase
44
- end
45
- end
46
- def platform
47
- NewRelic::Agent::Samplers::MemorySampler.platform
48
- end
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
- def stats
51
- stats_engine.get_stats("Memory/Physical", false)
52
- end
53
- def poll
54
- sample = @sampler.get_sample
55
- stats.record_data_point sample if sample
56
- stats
57
- end
58
- class Base
59
- def can_run?
60
- return false if @broken
61
- m = get_memory rescue nil
62
- m && m > 0
63
- end
64
- def get_sample
65
- return nil if @broken
66
- begin
67
- m = get_memory
68
- if m.nil?
69
- NewRelic::Agent.instance.log.error "Unable to get the resident memory for process #{$$}. Disabling memory sampler."
70
- @broken = true
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
- class JavaHeapSampler < Base
85
+ class JavaHeapSampler < Base
84
86
 
85
- def get_memory
86
- raise "Can't sample Java heap unless running in JRuby" unless defined? JRuby
87
- java.lang.Runtime.getRuntime.totalMemory / (1024 * 1024).to_f rescue nil
88
- end
89
- def to_s
90
- "JRuby Java heap sampler"
91
- end
92
- end
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
- class ShellPS < Base
95
- def initialize(command)
96
- super()
97
- @command = command
98
- end
99
- # Returns the amount of resident memory this process is using in MB
100
- #
101
- def get_memory
102
- process = $$
103
- memory = `#{@command} #{process}`.split("\n")[1].to_f / 1024.0 rescue nil
104
- # if for some reason the ps command doesn't work on the resident os,
105
- # then don't execute it any more.
106
- raise "Faulty command: `#{@command} #{process}`" if memory.nil? || memory <= 0
107
- memory
108
- end
109
- def to_s
110
- "shell command sampler: #{@command}"
111
- end
112
- end
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
- # ProcStatus
115
- #
116
- # A class that samples memory by reading the file /proc/$$/status, which is specific to linux
117
- #
118
- class ProcStatus < Base
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
- # Returns the amount of resident memory this process is using in MB
121
- #
122
- def get_memory
123
- File.open(proc_status_file, "r") do |f|
124
- while !f.eof?
125
- if f.readline =~ /RSS:\s*(\d+) kB/i
126
- return $1.to_f / 1024.0
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
@@ -1,3 +1,5 @@
1
+ require 'new_relic/agent/sampler'
2
+
1
3
  module NewRelic
2
4
  module Agent
3
5
  module Samplers
@@ -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 sampler
38
- periodic_samplers.each do |s|
39
- raise "Sampler #{sampler.object_id} is already registered. Don't call add_sampler directly anymore." if s.object_id == sampler.object_id
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 sampler
48
- harvest_samplers.each do |s|
49
- raise "Sampler #{sampler.id} is already registered. Don't call add_sampler directly anymore." if s.id == sampler.id
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 = GC.time
45
- @last_gc_count = GC.collections
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
- @@collecting_gc ||= GC.respond_to?(:time) && GC.respond_to?(:collections) && !NewRelic::Control.instance.multi_threaded?
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 = GC.collections - @last_gc_count
124
- elapsed = (GC.time - @last_gc_timestamp).to_f
125
- @last_gc_timestamp = GC.time
126
- @last_gc_count = GC.collections
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