em-monitor 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+
2
+ task :test do
3
+ exec 'ruby ./test/*.rb'
4
+ end
5
+
6
+ task :default => [:test]
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = 'em-monitor'
3
+ gem.version = '0.1'
4
+
5
+ gem.summary = 'Monitor the distribution of your eventmachine CPU usage'
6
+ gem.description = "EventLoops are awesome unless you're doing a lot of blocking CPU stuff, at which point they become useless.
7
+ This gem lets you easily graph the lengths of CPU-blocked spans so that you can take action to make your
8
+ eventmachine server faster"
9
+
10
+ gem.authors = ['Conrad Irwin']
11
+ gem.email = %w(conrad@rapportive.com)
12
+ gem.homepage = 'http://github.com/ConradIrwin/em-monitor'
13
+
14
+ gem.license = 'MIT'
15
+
16
+ gem.required_ruby_version = '>= 1.9.3'
17
+
18
+ gem.add_dependency 'eventmachine'
19
+ gem.add_dependency 'lspace'
20
+
21
+ gem.files = `git ls-files`.split("\n")
22
+ end
@@ -0,0 +1,29 @@
1
+ require 'em-monitor'
2
+ require 'time'
3
+
4
+ at_exit do
5
+ exec %(gnuplot -e 'load "example/stacked.plot"' -p)
6
+ end
7
+
8
+ puts "When you've gathered enough data (about 30s should be enough) hit <Ctrl-C>"
9
+
10
+ EM::run do
11
+ file = File.open("/tmp/to_plot", "w")
12
+
13
+ # Every second, output the % of time spent in CPU-spans of various lengths in a
14
+ # gnuplot-readable format.
15
+ EM::monitor_histogram(interval: 1, stacked: true) do |hist, from, to|
16
+ percentages = hist.map{ |k, v| (v * 100 / (to - from)) }
17
+
18
+ file.puts "#{Time.now.utc.iso8601} #{percentages.join(" ")}"
19
+ file.flush
20
+ puts "#{Time.now.utc.iso8601} #{percentages.map{ |x| "%0.2f" % x }.join(" ")}"
21
+ end
22
+
23
+ # Generate some sample data.
24
+ samples = [0.0005] * 100000 + [0.005] * 10000 + [0.05] * 1000 + [0.5] * 100 + [5] * 10 + [10] * 1
25
+ EM::PeriodicTimer.new(rand * 0.02) do
26
+ sleep samples.sample
27
+ end
28
+ end
29
+
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/gnuplot -persist
2
+ #
3
+ #
4
+ # See example/gnuplot.rb for how to use this!
5
+ #
6
+ #
7
+ #
8
+ #
9
+ # G N U P L O T
10
+ # Version 4.6 patchlevel 0 last modified 2012-03-04
11
+ # Build System: Linux x86_64
12
+ #
13
+ # Copyright (C) 1986-1993, 1998, 2004, 2007-2012
14
+ # Thomas Williams, Colin Kelley and many others
15
+ #
16
+ # gnuplot home: http://www.gnuplot.info
17
+ # faq, bugs, etc: type "help FAQ"
18
+ # immediate help: type "help" (plot window: hit 'h')
19
+ # set terminal wxt 0
20
+ # set output
21
+ unset clip points
22
+ set clip one
23
+ unset clip two
24
+ set bar 1.000000 front
25
+ set border 31 front linetype -1 linewidth 1.000
26
+ set timefmt z "%Y-%m-%dT%H:%M:%SZ"
27
+ set zdata
28
+ set timefmt y "%Y-%m-%dT%H:%M:%SZ"
29
+ set ydata
30
+ set timefmt x "%Y-%m-%dT%H:%M:%SZ"
31
+ set xdata time
32
+ set timefmt cb "%Y-%m-%dT%H:%M:%SZ"
33
+ set timefmt y2 "%Y-%m-%dT%H:%M:%SZ"
34
+ set y2data
35
+ set timefmt x2 "%Y-%m-%dT%H:%M:%SZ"
36
+ set x2data
37
+ set boxwidth
38
+ set style fill empty border
39
+ set style rectangle back fc lt -3 fillstyle solid 1.00 border lt -1
40
+ set style circle radius graph 0.02, first 0, 0
41
+ set style ellipse size graph 0.05, 0.03, first 0 angle 0 units xy
42
+ set dummy x,y
43
+ set format x "% g"
44
+ set format y "% g"
45
+ set format x2 "% g"
46
+ set format y2 "% g"
47
+ set format z "% g"
48
+ set format cb "% g"
49
+ set format r "% g"
50
+ set angles radians
51
+ unset grid
52
+ set raxis
53
+ set key title ""
54
+ set key inside right top vertical Right noreverse enhanced autotitles nobox
55
+ set key noinvert samplen 4 spacing 1 width 0 height 0
56
+ set key maxcolumns 0 maxrows 0
57
+ set key noopaque
58
+ unset label
59
+ unset arrow
60
+ set style increment default
61
+ unset style line
62
+ unset style arrow
63
+ set style histogram clustered gap 2 title offset character 0, 0, 0
64
+ unset logscale
65
+ set offsets 0, 0, 0, 0
66
+ set pointsize 1
67
+ set pointintervalbox 1
68
+ set encoding default
69
+ unset polar
70
+ unset parametric
71
+ unset decimalsign
72
+ set view 60, 30, 1, 1
73
+ set samples 100, 100
74
+ set isosamples 10, 10
75
+ set surface
76
+ unset contour
77
+ set clabel '%8.3g'
78
+ set mapping cartesian
79
+ set datafile separator whitespace
80
+ unset hidden3d
81
+ set cntrparam order 4
82
+ set cntrparam linear
83
+ set cntrparam levels auto 5
84
+ set cntrparam points 5
85
+ set size ratio 0 1,1
86
+ set origin 0,0
87
+ set style data points
88
+ set style function lines
89
+ set xzeroaxis linetype -2 linewidth 1.000
90
+ set yzeroaxis linetype -2 linewidth 1.000
91
+ set zzeroaxis linetype -2 linewidth 1.000
92
+ set x2zeroaxis linetype -2 linewidth 1.000
93
+ set y2zeroaxis linetype -2 linewidth 1.000
94
+ set ticslevel 0.5
95
+ set mxtics default
96
+ set mytics default
97
+ set mztics default
98
+ set mx2tics default
99
+ set my2tics default
100
+ set mcbtics default
101
+ set xtics border in scale 1,0.5 mirror norotate offset character 0, 0, 0 autojustify
102
+ set xtics autofreq norangelimit
103
+ set ytics border in scale 1,0.5 mirror norotate offset character 0, 0, 0 autojustify
104
+ set ytics autofreq norangelimit
105
+ set ztics border in scale 1,0.5 nomirror norotate offset character 0, 0, 0 autojustify
106
+ set ztics autofreq norangelimit
107
+ set nox2tics
108
+ set noy2tics
109
+ set cbtics border in scale 1,0.5 mirror norotate offset character 0, 0, 0 autojustify
110
+ set cbtics autofreq norangelimit
111
+ set rtics axis in scale 1,0.5 nomirror norotate offset character 0, 0, 0 autojustify
112
+ set rtics autofreq norangelimit
113
+ set title "% of time spent in CPU-spans of X or less"
114
+ set title offset character 0, 0, 0 font "" norotate
115
+ set timestamp bottom
116
+ set timestamp ""
117
+ set timestamp offset character 0, 0, 0 font "" norotate
118
+ set rrange [ * : * ] noreverse nowriteback
119
+ set trange [ * : * ] noreverse nowriteback
120
+ set urange [ * : * ] noreverse nowriteback
121
+ set vrange [ * : * ] noreverse nowriteback
122
+ set xlabel "time of day"
123
+ set xlabel offset character 0, 0, 0 font "" textcolor lt -1 norotate
124
+ set x2label ""
125
+ set x2label offset character 0, 0, 0 font "" textcolor lt -1 norotate
126
+ set xrange [ * : * ] noreverse nowriteback
127
+ set x2range [ * : * ] noreverse nowriteback
128
+ set ylabel "%"
129
+ set ylabel offset character 0, 0, 0 font "" textcolor lt -1 rotate by -270
130
+ set y2label ""
131
+ set y2label offset character 0, 0, 0 font "" textcolor lt -1 rotate by -270
132
+ set yrange [ * : * ] noreverse nowriteback
133
+ set y2range [ * : * ] noreverse nowriteback
134
+ set zlabel ""
135
+ set zlabel offset character 0, 0, 0 font "" textcolor lt -1 norotate
136
+ set zrange [ * : * ] noreverse nowriteback
137
+ set cblabel ""
138
+ set cblabel offset character 0, 0, 0 font "" textcolor lt -1 rotate by -270
139
+ set cbrange [ * : * ] noreverse nowriteback
140
+ set zero 1e-08
141
+ set lmargin -1
142
+ set bmargin -1
143
+ set rmargin -1
144
+ set tmargin -1
145
+ set locale "en_US.UTF-8"
146
+ set pm3d explicit at s
147
+ set pm3d scansautomatic
148
+ set pm3d interpolate 1,1 flush begin noftriangles nohidden3d corners2color mean
149
+ set palette positive nops_allcF maxcolors 0 gamma 1.5 color model RGB
150
+ set palette rgbformulae 7, 5, 15
151
+ set colorbox default
152
+ set colorbox vertical origin screen 0.9, 0.2, 0 size screen 0.05, 0.6, 0 front bdefault
153
+ set style boxplot candles range 1.50 outliers pt 7 separation 1 labels auto unsorted
154
+ set loadpath
155
+ set fontpath
156
+ set psdir
157
+ set fit noerrorvariables
158
+ GNUTERM = "wxt"
159
+ plot '/tmp/to_plot' u 1:7 t 'inf' w filledcurves x1, '/tmp/to_plot' u 1:6 t '10' w filledcurves x1, '/tmp/to_plot' u 1:5 t '1' w filledcurves x1, '/tmp/to_plot' u 1:4 t '0.1' w filledcurves x1, '/tmp/to_plot' u 1:3 t '0.01' w filledcurves x1, '/tmp/to_plot' u 1:2 t '0.001' w filledcurves x1
160
+ # EOF
data/lib/em-monitor.rb ADDED
@@ -0,0 +1,202 @@
1
+ require 'lspace/eventmachine'
2
+
3
+ # EM::Monitor adds a few methods to eventmachine that can help you
4
+ # keep track of 'lag' caused by long-running CPU spans on the reactor thread.
5
+ module EventMachine
6
+
7
+ class << self
8
+ alias_method :run_without_monitor, :run
9
+ private :run_without_monitor
10
+ end
11
+
12
+ # Run the eventmachine reactor with monitoring.
13
+ def self.run(*args, &block)
14
+ run_without_monitor(*args) do |*a, &b|
15
+ EM::Monitor.new do |monitor|
16
+ @monitor = monitor
17
+ block.call(*a, &b) if block_given?
18
+ end
19
+ end
20
+ ensure
21
+ @monitor = nil
22
+ end
23
+
24
+ # Set the block to be called periodically with timing data.
25
+ #
26
+ # @param [Hash] opts Configuration
27
+ # @param [Proc] block the block to call.
28
+ #
29
+ # @option opts [Number] :interval (60)
30
+ # The approximate number of seconds between calls to your block.
31
+ # If your event loop regularly runs long CPU-spans then the actual time between
32
+ # calls can vary significantly.
33
+ #
34
+ # @yieldparam [Array<Float>] spans The number of seconds spent by each CPU-span in the
35
+ # monitoring interval
36
+ # @yieldparam [Time] from The start of the monitoring interval
37
+ # @yieldparam [Time] to The end of the monitoring interval
38
+ #
39
+ # @example
40
+ # EM::monitor_spans do |spans, from, to|
41
+ # puts "Used %.2f seconds of CPU in the last %.2f seconds." % (spans.inject(&:+), to - from)
42
+ # end
43
+ def self.monitor_spans(opts = {}, &block)
44
+ raise "EventMachine not initialized" unless @monitor
45
+ @monitor.monitor_spans(opts[:interval] || Monitor::DEFAULT_INTERVAL, &block)
46
+ end
47
+
48
+ # Set the block to be called periodically with histogram data.
49
+ #
50
+ # This is a convenience wrapper around {monitor_spans} for the common use-case of
51
+ # wanting to plot a histogram of loop utilisation split into time-chunks.
52
+ #
53
+ # In the normal case you can plot these values directly and it will tell you
54
+ # how much CPU-time was used by spans shorter than a given length. However, care
55
+ # should be taken if CPU-spans are often of similar length to :interval. This can
56
+ # cause the actual delay between calls to the block to vary significantly and so
57
+ # if you're trying to plot a line of CPU-utilization then it can give you misleading
58
+ # answers. If this is a concern to you then you might want to use the :cumulative
59
+ # mode and ask your graphing library to plot the derivative of the values over time.
60
+ #
61
+ # @param [Hash] opts Configuration for the histogram
62
+ # @param [Proc] block the block to call.
63
+ #
64
+ # @option opts [Number] :interval (60)
65
+ # The approximate number of seconds between calls to your block.
66
+ # If your event loop regularly runs long CPU-spans then the actual time between
67
+ # calls can vary significantly.
68
+ #
69
+ # @option opts [Array<Number>] :buckets ([0.001, 0.01, 0.1, 1, 10, Infinity])
70
+ # The boundaries of the histogram buckets, Infinity will be added even if it's not
71
+ # specified by you to ensure that all spans are included.
72
+ # CPU-spans are put into the smallest bucket with a limit longer than the span.
73
+ #
74
+ # @option opts [Boolean] :stacked (false)
75
+ # When true larger buckets include the sum of all smaller buckets in addition to
76
+ # the number of seconds spent in CPU-spans that fell into that bucket directly.
77
+ # When false each CPU-span will be put into exactly one bucket
78
+ #
79
+ # @option opts [Boolean] :cumulative (false)
80
+ # When true the values of each bucket will increase monotonically and the
81
+ # derivative over time can be used to tell how much CPU was used by spans
82
+ # in each bucket in the current monitoring interval.
83
+ # When false the values of each bucket are only the amount of time spent
84
+ # by CPU-spans in that bucket in the current monitoring interval.
85
+ #
86
+ # @yieldparam [Hash<Float,Float>] histogram A histogram from bucket-size to
87
+ # amount of CPU-time spent in that bucket.
88
+ # @yieldparam [Time] from The start of the monitoring interval
89
+ # @yieldparam [Time] to The end of the monitoring interval
90
+ #
91
+ # @example
92
+ # # Create an input file suitable for feeding to gnuplot.
93
+ # #
94
+ # EM::monitor_histogram(stacked: true) do |hist, from, to|
95
+ # gnuplot_input.puts #{to.iso8601} #{hist.values.join(" ")}"
96
+ # end
97
+ def self.monitor_histogram(opts = {}, &block)
98
+ stacked = !!opts[:stacked]
99
+ cumulative = !!opts[:cumulative]
100
+ interval = opts[:interval] || Monitor::DEFAULT_INTERVAL
101
+ buckets = (opts[:buckets] || Monitor::DEFAULT_BUCKETS).sort
102
+ buckets << (1/0.0)
103
+
104
+ # ensure the histogram keys are in the right order
105
+ hist = buckets.each_with_object({}){ |bucket, h| h[bucket] = 0 }
106
+
107
+ monitor_spans(opts) do |spans, from, to|
108
+
109
+ unless cumulative
110
+ hist = buckets.each_with_object({}){ |bucket, h| h[bucket] = 0 }
111
+ end
112
+
113
+ if stacked
114
+ spans.each do |span|
115
+ buckets.each do |bucket|
116
+ hist[bucket] += span if bucket > span
117
+ end
118
+ end
119
+ else
120
+ spans.each do |span|
121
+ hist[buckets.detect{ |bucket| bucket > span }] += span
122
+ end
123
+ end
124
+
125
+ block.call hist, from, to
126
+ end
127
+ end
128
+
129
+ # The monitor object itself deals with maintaining lspace and
130
+ # timers.
131
+ class Monitor
132
+ # How long (by default) between calls to monitors
133
+ DEFAULT_INTERVAL = 60
134
+ # Which buckets to include in the histogram by default
135
+ DEFAULT_BUCKETS = [0.001, 0.01, 0.1, 1, 10]
136
+
137
+ # Create a new monitor.
138
+ #
139
+ # The block will be called in the monitor's LSpace, all further
140
+ # EM work should happen within the block to ensure that we measure
141
+ # everything.
142
+ #
143
+ # @param [Proc] block The block during which to monitor events
144
+ # @yieldparam [Monitor] self
145
+ def initialize(&block)
146
+ create_lspace.enter do
147
+ block.call self
148
+ end
149
+ end
150
+
151
+ # Attach a listener to this monitor.
152
+ #
153
+ # Only one listener can be active at a time, and the interval set
154
+ # here will take affect after the next tick of the previous interval.
155
+ #
156
+ # @param [Number] interval The (lower bound of) time in seconds between calls to block
157
+ # @param [Proc] block The block to call
158
+ # @yieldparam [Array<Float>] spans Each number of seconds the event loop spent processing.
159
+ # @yieldparam [Time] from The time at which the block was previously called
160
+ # @yieldparam [Time] to The current time
161
+ # @see EM.monitor_spans
162
+ def monitor_spans(interval, &block)
163
+ @periodic_timer ||= create_timer(interval)
164
+ @periodic_timer.interval = interval
165
+ @monitor = block
166
+ end
167
+
168
+ private
169
+
170
+ # Add an around_filter to LSpace that wraps every CPU-span in a timing function.
171
+ #
172
+ # @return [LSpace]
173
+ def create_lspace
174
+ LSpace.new.around_filter do |&block|
175
+ start = Time.now
176
+ begin
177
+ block.call
178
+ ensure
179
+ @timings << Time.now - start if @timings
180
+ end
181
+ end
182
+ end
183
+
184
+ # Add a periodic timer that will periodically call @monitor with the contents
185
+ # of @timings.
186
+ #
187
+ # @param [Number] interval The interval to use for the timer.
188
+ # @return [EM::PeriodicTimer]
189
+ def create_timer(interval)
190
+ @timings = []
191
+ time = Time.now
192
+ EM::PeriodicTimer.new(interval) do
193
+ # Set last_time to *before* we call the monitor.
194
+ # This is because the duration of the monitor will be included in the next set of
195
+ # timings.
196
+ last_time, time = time, Time.now
197
+ timings = @timings.slice!(0..-1)
198
+ @monitor.call timings, last_time, time
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,232 @@
1
+ $: << './lib'
2
+ require 'em-monitor'
3
+ require 'minitest/autorun'
4
+ require 'pry'
5
+
6
+ class TestEmMonitor < MiniTest::Unit::TestCase
7
+
8
+ def test_before_running
9
+ e = assert_raises(RuntimeError){ EM::monitor_spans }
10
+ assert_equal "EventMachine not initialized", e.message
11
+ end
12
+
13
+ def test_monitor_spans
14
+ spans = from = to = nil
15
+
16
+ EM::run do
17
+ EM::monitor_spans(interval: 0.1) do |*a|
18
+ spans, from, to = a
19
+ EM::stop
20
+ end
21
+ 10.times do
22
+ EM::next_tick{ sleep 0.005 }
23
+ end
24
+ sleep 0.05
25
+ end
26
+
27
+ assert_equal spans.size, 11
28
+ assert_in_delta spans.first, 0.05
29
+
30
+ spans.drop(1).each do |span|
31
+ assert_in_delta span, 0.005
32
+ end
33
+
34
+ assert_in_delta(to - from, 0.1, 0.002)
35
+ end
36
+
37
+ def test_span_raising_exception
38
+ spans = from = to = nil
39
+ EM::run do
40
+ EM::monitor_spans(interval: 0.1) do |*a|
41
+ spans, from, to = a
42
+ EM::stop
43
+ end
44
+ EM::error_handler do
45
+ sleep 0.006
46
+ end
47
+
48
+ EM::next_tick do
49
+ sleep 0.004
50
+ raise "oops"
51
+ end
52
+
53
+ sleep 0.002
54
+ end
55
+
56
+ assert_in_delta spans[0], 0.002
57
+ assert_in_delta spans[1], 0.004
58
+ assert_in_delta spans[2], 0.006
59
+ end
60
+
61
+ def test_monitor_histograms
62
+ histogram = from = to = nil
63
+
64
+ EM::run do
65
+ EM::monitor_histogram(interval: 0.6, buckets: [0.01, 0.02, 0.03, 0.04]) do |*a|
66
+ histogram, from, to = a
67
+ EM::stop
68
+ end
69
+ 4.times do |i|
70
+ EM::next_tick{
71
+ 5.times do
72
+ EM::next_tick{ sleep(0.005 + i * 0.01) }
73
+ end
74
+ }
75
+ end
76
+ sleep 0.055
77
+ end
78
+
79
+ assert_equal histogram.keys, [0.01, 0.02, 0.03, 0.04, 1/0.0]
80
+
81
+ assert_in_delta histogram[0.01], 0.025
82
+ assert_in_delta histogram[0.02], 0.075
83
+ assert_in_delta histogram[0.03], 0.125
84
+ assert_in_delta histogram[0.04], 0.175
85
+ assert_in_delta histogram[1 / 0.0], 0.055
86
+ assert_in_delta(to - from, 0.6, 0.002)
87
+ end
88
+
89
+ def test_stacked_histogram
90
+ histogram = from = to = nil
91
+
92
+ EM::run do
93
+ EM::monitor_histogram(interval: 0.5, buckets: [0.01, 0.02, 0.03, 0.04], stacked: true) do |*a|
94
+ histogram, from, to = a
95
+ EM::stop
96
+ end
97
+ 4.times do |i|
98
+ EM::next_tick{
99
+ 5.times do
100
+ EM::next_tick{ sleep(0.005 + i * 0.01) }
101
+ end
102
+ }
103
+ end
104
+ sleep 0.055
105
+ end
106
+
107
+ assert_equal histogram.keys, [0.01, 0.02, 0.03, 0.04, 1/0.0]
108
+
109
+ assert_in_delta histogram[0.01], 0.025, 0.01
110
+ assert_in_delta histogram[0.02], 0.100, 0.01
111
+ assert_in_delta histogram[0.03], 0.225, 0.01
112
+ assert_in_delta histogram[0.04], 0.400, 0.01
113
+ assert_in_delta histogram[1 / 0.0], 0.455, 0.01
114
+ assert_in_delta(to - from, 0.5, 0.02)
115
+ end
116
+
117
+ def test_cumulative_histogram
118
+ histogram = from = to = nil
119
+ histogram2 = from2 = to2 = nil
120
+ second_time = false
121
+
122
+ EM::run do
123
+ trigger = lambda do
124
+ 4.times do |i|
125
+ EM::next_tick{
126
+ 5.times do
127
+ EM::next_tick{ sleep(0.005 + i * 0.01) }
128
+ end
129
+ }
130
+ end
131
+ end
132
+ EM::monitor_histogram(interval: 0.5, buckets: [0.01, 0.02, 0.03, 0.04], cumulative: true) do |*a|
133
+ if second_time
134
+ histogram2, from2, to2 = a
135
+ EM::stop
136
+ else
137
+ histogram, from, to = a
138
+ histogram = histogram.dup
139
+ second_time = true
140
+ trigger.()
141
+ sleep 0.066
142
+ end
143
+ end
144
+ trigger.()
145
+ sleep 0.055
146
+ end
147
+
148
+ assert_equal histogram.keys, [0.01, 0.02, 0.03, 0.04, 1/0.0]
149
+
150
+ assert_in_delta histogram[0.01], 0.025, 0.01
151
+ assert_in_delta histogram[0.02], 0.075, 0.01
152
+ assert_in_delta histogram[0.03], 0.125, 0.01
153
+ assert_in_delta histogram[0.04], 0.175, 0.01
154
+ assert_in_delta histogram[1 / 0.0], 0.055, 0.01
155
+ assert_in_delta(to - from, 0.5, 0.02)
156
+
157
+ assert_equal to, from2
158
+
159
+ assert_in_delta histogram2[0.01], 0.025 * 2, 0.01
160
+ assert_in_delta histogram2[0.02], 0.075 * 2, 0.01
161
+ assert_in_delta histogram2[0.03], 0.125 * 2, 0.01
162
+ assert_in_delta histogram2[0.04], 0.175 * 2, 0.01
163
+ assert_in_delta histogram2[1 / 0.0], 0.055 + 0.066, 0.01
164
+ assert_in_delta(to2 - from2, 0.566, 0.01)
165
+ end
166
+
167
+ def test_cumulative_stacked_histogram
168
+ histogram = from = to = nil
169
+ histogram2 = from2 = to2 = nil
170
+ second_time = false
171
+
172
+ EM::run do
173
+ trigger = lambda do
174
+ 4.times do |i|
175
+ EM::next_tick{
176
+ 5.times do
177
+ EM::next_tick{ sleep(0.005 + i * 0.01) }
178
+ end
179
+ }
180
+ end
181
+ end
182
+ EM::monitor_histogram(interval: 0.5, buckets: [0.01, 0.02, 0.03, 0.04], cumulative: true, stacked: true) do |*a|
183
+ if second_time
184
+ histogram2, from2, to2 = a
185
+ EM::stop
186
+ else
187
+ histogram, from, to = a
188
+ histogram = histogram.dup
189
+ second_time = true
190
+ trigger.()
191
+ sleep 0.066
192
+ end
193
+ end
194
+ trigger.()
195
+ sleep 0.055
196
+ end
197
+
198
+ assert_equal histogram.keys, [0.01, 0.02, 0.03, 0.04, 1/0.0]
199
+
200
+ assert_in_delta histogram[0.01], 0.025, 0.01
201
+ assert_in_delta histogram[0.02], 0.100, 0.01
202
+ assert_in_delta histogram[0.03], 0.225, 0.01
203
+ assert_in_delta histogram[0.04], 0.400, 0.01
204
+ assert_in_delta histogram[1 / 0.0], 0.455, 0.01
205
+ assert_in_delta(to - from, 0.5, 0.02)
206
+
207
+ assert_equal to, from2
208
+
209
+ assert_in_delta histogram2[0.01], 0.025 * 2, 0.01
210
+ assert_in_delta histogram2[0.02], 0.100 * 2, 0.01
211
+ assert_in_delta histogram2[0.03], 0.225 * 2, 0.01
212
+ assert_in_delta histogram2[0.04], 0.400 * 2, 0.01
213
+ assert_in_delta histogram2[1 / 0.0], 0.455 + 0.466, 0.01
214
+ assert_in_delta(to2 - from2, 0.566, 0.01)
215
+ end
216
+
217
+ def test_span_longer_than_interval
218
+ spans = from = to = nil
219
+
220
+ EM::run do
221
+ EM::monitor_spans(interval: 0.001) do |*a|
222
+ spans, from, to = a
223
+ EM::stop
224
+ end
225
+ EM::next_tick{ sleep 0.005 }
226
+ end
227
+
228
+ assert_equal spans.size, 2
229
+ assert_in_delta(spans.last, 0.005)
230
+ assert_in_delta(to - from, 0.005, 0.002)
231
+ end
232
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-monitor
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: '0.1'
6
+ platform: ruby
7
+ authors:
8
+ - Conrad Irwin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ type: :runtime
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ! '>='
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ name: eventmachine
30
+ - !ruby/object:Gem::Dependency
31
+ type: :runtime
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ name: lspace
46
+ description: ! "EventLoops are awesome unless you're doing a lot of blocking CPU stuff,
47
+ at which point they become useless.\n This gem lets you easily
48
+ graph the lengths of CPU-blocked spans so that you can take action to make your\n
49
+ \ eventmachine server faster"
50
+ email:
51
+ - conrad@rapportive.com
52
+ executables: []
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - Rakefile
57
+ - em-monitor.gemspec
58
+ - example/gnuplot.rb
59
+ - example/stacked.plot
60
+ - lib/em-monitor.rb
61
+ - test/test_em-monitor.rb
62
+ homepage: http://github.com/ConradIrwin/em-monitor
63
+ licenses:
64
+ - MIT
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: 1.9.3
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 1.8.24
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Monitor the distribution of your eventmachine CPU usage
87
+ test_files: []
88
+ has_rdoc: