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 +6 -0
- data/em-monitor.gemspec +22 -0
- data/example/gnuplot.rb +29 -0
- data/example/stacked.plot +160 -0
- data/lib/em-monitor.rb +202 -0
- data/test/test_em-monitor.rb +232 -0
- metadata +88 -0
data/Rakefile
ADDED
data/em-monitor.gemspec
ADDED
@@ -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
|
data/example/gnuplot.rb
ADDED
@@ -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:
|