em-monitor 0.1

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.
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: