solutious-stella 0.5.5

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.
Files changed (60) hide show
  1. data/CHANGES.txt +36 -0
  2. data/README.textile +162 -0
  3. data/Rakefile +88 -0
  4. data/bin/stella +12 -0
  5. data/bin/stella.bat +12 -0
  6. data/lib/daemonize.rb +56 -0
  7. data/lib/pcaplet.rb +180 -0
  8. data/lib/stella.rb +101 -0
  9. data/lib/stella/adapter/ab.rb +337 -0
  10. data/lib/stella/adapter/base.rb +106 -0
  11. data/lib/stella/adapter/httperf.rb +305 -0
  12. data/lib/stella/adapter/pcap_watcher.rb +221 -0
  13. data/lib/stella/adapter/proxy_watcher.rb +76 -0
  14. data/lib/stella/adapter/siege.rb +341 -0
  15. data/lib/stella/cli.rb +258 -0
  16. data/lib/stella/cli/agents.rb +73 -0
  17. data/lib/stella/cli/base.rb +55 -0
  18. data/lib/stella/cli/language.rb +18 -0
  19. data/lib/stella/cli/localtest.rb +78 -0
  20. data/lib/stella/cli/sysinfo.rb +16 -0
  21. data/lib/stella/cli/watch.rb +278 -0
  22. data/lib/stella/command/base.rb +40 -0
  23. data/lib/stella/command/localtest.rb +358 -0
  24. data/lib/stella/data/domain.rb +82 -0
  25. data/lib/stella/data/http.rb +131 -0
  26. data/lib/stella/logger.rb +84 -0
  27. data/lib/stella/response.rb +85 -0
  28. data/lib/stella/storable.rb +201 -0
  29. data/lib/stella/support.rb +276 -0
  30. data/lib/stella/sysinfo.rb +257 -0
  31. data/lib/stella/test/definition.rb +79 -0
  32. data/lib/stella/test/run/summary.rb +70 -0
  33. data/lib/stella/test/stats.rb +114 -0
  34. data/lib/stella/text.rb +64 -0
  35. data/lib/stella/text/resource.rb +38 -0
  36. data/lib/utils/crypto-key.rb +84 -0
  37. data/lib/utils/domainutil.rb +47 -0
  38. data/lib/utils/escape.rb +302 -0
  39. data/lib/utils/fileutil.rb +78 -0
  40. data/lib/utils/httputil.rb +266 -0
  41. data/lib/utils/mathutil.rb +15 -0
  42. data/lib/utils/stats.rb +88 -0
  43. data/lib/utils/textgraph.rb +267 -0
  44. data/lib/utils/timerutil.rb +58 -0
  45. data/lib/win32/Console.rb +970 -0
  46. data/lib/win32/Console/ANSI.rb +305 -0
  47. data/support/kvm.h +91 -0
  48. data/support/ruby-pcap-takuma-notes.txt +19 -0
  49. data/support/ruby-pcap-takuma-patch.txt +30 -0
  50. data/support/text/en.yaml +80 -0
  51. data/support/text/nl.yaml +7 -0
  52. data/support/useragents.txt +75 -0
  53. data/tests/01-util_test.rb +0 -0
  54. data/tests/02-stella-util_test.rb +42 -0
  55. data/tests/10-stella_test.rb +104 -0
  56. data/tests/11-stella-storable_test.rb +68 -0
  57. data/tests/60-stella-command_test.rb +248 -0
  58. data/tests/80-stella-cli_test.rb +45 -0
  59. data/tests/spec-helper.rb +31 -0
  60. metadata +165 -0
@@ -0,0 +1,305 @@
1
+
2
+
3
+ module Stella
4
+ module Adapter
5
+
6
+ #Usage: httperf [-hdvV] [--add-header S] [--burst-length N] [--client N/N]
7
+ # [--close-with-reset] [--debug N] [--failure-status N]
8
+ # [--help] [--hog] [--http-version S] [--max-connections N]
9
+ # [--max-piped-calls N] [--method S] [--no-host-hdr]
10
+ # [--num-calls N] [--num-conns N] [--period [d|u|e]T1[,T2]]
11
+ # [--port N] [--print-reply [header|body]] [--print-request [header|body]]
12
+ # [--rate X] [--recv-buffer N] [--retry-on-failure] [--send-buffer N]
13
+ # [--server S] [--server-name S] [--session-cookies]
14
+ # [--ssl] [--ssl-ciphers L] [--ssl-no-reuse]
15
+ # [--think-timeout X] [--timeout X] [--uri S] [--verbose] [--version]
16
+ # [--wlog y|n,file] [--wsess N,N,X] [--wsesslog N,X,file]
17
+ # [--wset N,X]
18
+ #
19
+ class Httperf < Stella::Adapter::Base
20
+
21
+
22
+
23
+ attr_accessor :hog, :server, :uri, :num_conns, :num_calls, :rate, :timeout, :think_timeout, :port
24
+ attr_accessor :burst_length, :client, :close_with_reset, :debug, :failure_status
25
+ attr_accessor :help, :http_version, :max_connections, :max_piped_calls, :method, :no_host_hdr
26
+ attr_accessor :period, :print_reply, :print_request, :recv_buffer, :retry_on_failure, :send_buffer
27
+ attr_accessor :server_name, :session_cookies, :ssl, :ssl_ciphers, :ssl_no_reuse, :verbose
28
+
29
+ attr_writer :version, :add_header, :wlog, :wsess, :wsesslog, :wset
30
+
31
+ def initialize(options={}, arguments=[])
32
+
33
+ @name = 'httperf'
34
+ @private_variables = ['private_variables', 'name', 'arguments', 'load_factor', 'working_directory']
35
+ @load_factor = 1
36
+
37
+ super(options, arguments)
38
+ end
39
+
40
+
41
+
42
+ def error
43
+ (File.exists? stderr_path) ? FileUtil.read_file(stderr_path) : "Unknown error"
44
+ end
45
+
46
+ # Before calling run
47
+ def before
48
+
49
+
50
+ end
51
+ def command
52
+ raise CommandNotReady.new(self.class.to_s) unless ready?
53
+
54
+ command = "#{@name} "
55
+
56
+ instance_variables.each do |name|
57
+ canon = name.tr('@', '') # instance_variables returns '@name'
58
+ next if @private_variables.member?(canon)
59
+
60
+ # It's important that we take the value from the getter method
61
+ # because it applies the load factor.
62
+ value = self.send(canon)
63
+ if (value.is_a? Array)
64
+ value.each { |el| command << "--#{canon.tr('_', '-')} #{EscapeUtil.shell_single_word(el.to_s)} " }
65
+ else
66
+ command << "--#{canon.tr('_', '-')} #{EscapeUtil.shell_single_word(value.to_s)} "
67
+ end
68
+
69
+ end
70
+
71
+ command << (@arguments.map { |uri| "'#{uri}'" }).join(' ') unless @arguments.empty?
72
+ command
73
+ end
74
+
75
+ # After calling run
76
+ def after
77
+
78
+
79
+ end
80
+
81
+ def process_arguments(arguments)
82
+
83
+ opts = OptionParser.new
84
+ opts.on('--hog') do @hog = true end
85
+ opts.on('--server=S', String) do |v| @server = v end
86
+ opts.on('--server-name=S', String) do |v| @server_name = v end
87
+ opts.on('--port=N', Integer) do |v| @port = v end
88
+ opts.on('--uri=S', String) do |v| @uri = v end
89
+ opts.on('--num-conns=N', Integer) do |v| @num_conns = v end
90
+ opts.on('--num-calls=N', Integer) do |v| @num_calls = v end
91
+ opts.on('--rate=N', Integer) do |v| @rate = v end
92
+ opts.on('--timeout=N', Integer) do |v| @timeout = v end
93
+ opts.on('--think-timeout=N', Integer) do |v| @think_timeout = v end
94
+
95
+ opts.on('-h', '--help') do |v| @help = true end
96
+ opts.on('-v', '--verbose') do |v| @verbose = true end
97
+ opts.on('-V', '--version') do |v| @version = true end
98
+ opts.on('--close-with-reset') do |v| @close_with_reset = true end
99
+ opts.on('--session-cookies') do |v| @session_cookies = true end
100
+ opts.on('--ssl') do |v| @ssl = true end
101
+ opts.on('--ssl-ciphers') do |v| @ssl_ciphers = true end
102
+ opts.on('--ssl-no-reuse') do |v| @ssl_no_reuse = true end
103
+ opts.on('--no-host-hdr') do |v| @no_host_hdr = true end
104
+ opts.on('--retry-on-failure') do |v| @retry_on_failure = true end
105
+
106
+ opts.on('--add-header=S', String) do |v| @add_header ||= []; @add_header << v; end
107
+ opts.on('--burst-length=N', Integer) do |v| @burst_length = v end
108
+ opts.on('--client=S', String) do |v| @client = v end
109
+ opts.on('-d N', '--debug=N', Integer) do |v| @debug ||= 0; @debug = v end
110
+ opts.on('--failure-status=N', Integer) do |v| @failure_status = v end
111
+
112
+ opts.on('--http-version=S', String) do |v| @http_version = v end
113
+
114
+ opts.on('--max-connections=N', Integer) do |v| @max_connections = v end
115
+ opts.on('--max-piped-calls=N', Integer) do |v| @max_piped_calls = v end
116
+ opts.on('--method=S', String) do |v| @method = v end
117
+
118
+ opts.on('--period=S', String) do |v| @period = v end # TODO: Requires parsing
119
+ opts.on('--print-reply=[S]', String) do |v| @print_reply = v end
120
+ opts.on('--print-request=[S]', String) do |v| @print_request = v end
121
+
122
+ opts.on('--recv-buffer=N', Integer) do |v| @recv_buffer = v end
123
+ opts.on('--send-buffer=N', Integer) do |v| @send_buffer = v end
124
+
125
+
126
+ opts.on('--wlog=S', String) do |v| @wlog = Stella::Util::expand_str(v) end
127
+ opts.on('--wsess=S', String) do |v| @wsess = Stella::Util::expand_str(v, Integer) end
128
+ opts.on('--wsesslog=S', String) do |v| @wsesslog = Stella::Util::expand_str(v) end
129
+ opts.on('--wset=S', String) do |v| @wset = Stella::Util::expand_str(v) end
130
+
131
+ if @wsess
132
+
133
+ end
134
+
135
+ # parse! removes the options it finds.
136
+ # It also fails when it finds unknown switches (i.e. -X)
137
+ # Which should leave only the remaining arguments (URIs in this case)
138
+ opts.parse!(arguments)
139
+
140
+ self.arguments = arguments
141
+
142
+ rescue OptionParser::InvalidOption => ex
143
+ # We want to replace this text so we grab just the name of the argument
144
+ badarg = ex.message.gsub('invalid option: ', '')
145
+ raise InvalidArgument.new(badarg)
146
+ end
147
+
148
+
149
+ def version
150
+ vsn = 0
151
+ Stella::Util.capture_output("#{@name} --version") do |stdout, stderr|
152
+ stdout.join.scan(/httperf\-([\d\.]+)\s/) { |v| vsn = v[0] }
153
+ end
154
+ vsn
155
+ end
156
+
157
+ # loadtest
158
+ #
159
+ # True or false: is the call to siege a load test? If it's a call to help or version or
160
+ # to display the config this with return false. It's no reason for someone to make this
161
+ # call through Stella but it's here for goodness sake.
162
+ def loadtest?
163
+ @uri && !@uri.empty?
164
+ end
165
+ def ready?
166
+ @name && !instance_variables.empty?
167
+ end
168
+
169
+ def add_header(name=false, value=false)
170
+ # This is a hack since we have an instance variable called add_header.
171
+ # I figure this is the best of two evils because I'd rather keep the
172
+ # instance variable naming consistent.
173
+ return @add_header if !name && !value
174
+ @add_header ||= []
175
+ @add_header << "#{name}: #{value}"
176
+ end
177
+
178
+ def user_agent=(list=[])
179
+ return unless list && !list.empty?
180
+ list = list.to_ary
181
+ list.each do |agent|
182
+ add_header("User-Agent", agent)
183
+ end
184
+ end
185
+ def vusers
186
+ @wsess[1]
187
+ end
188
+ def vuser_rate
189
+ "#{vusers}/#{rate}"
190
+ end
191
+
192
+ def vusers=(v)
193
+ 0
194
+ end
195
+ def requests
196
+ @num_conns || (@wsess[0] * @wsess[1])
197
+ end
198
+ def requests=(v)
199
+ 0
200
+ end
201
+ def vuser_requests
202
+ 0
203
+ end
204
+ def wsess
205
+ @wsess.join(',')
206
+ end
207
+
208
+ def wset
209
+ @wset.join(',')
210
+ end
211
+
212
+
213
+ def wsesslog
214
+ @wsesslog.join(',')
215
+ end
216
+ def wlog
217
+ @wlog.join(',')
218
+ end
219
+
220
+ #def concurrent
221
+ # (@concurrent * @load_factor).to_i
222
+ #end
223
+ #def concurrent_f
224
+ # (@concurrent * @load_factor).to_f
225
+ #end
226
+ #def reps
227
+ # @reps
228
+ #end
229
+
230
+
231
+
232
+ # Siege writes the summary to STDERR
233
+ def summary_file
234
+ File.new(stdout_path) if File.exists?(stdout_path)
235
+ end
236
+
237
+ def rc_file
238
+ File.join(@working_directory, "siegerc")
239
+ end
240
+
241
+ def log_file
242
+ File.join(@working_directory, "siege.log")
243
+ end
244
+
245
+ def uris_file
246
+ File.join(@working_directory, File.basename(@file))
247
+ end
248
+
249
+ # httperf --hog --timeout=30 --client=0/1 --server=127.0.0.1 --port=5600 --uri=/ --send-buffer=4096 --recv-buffer=16384 --num-conns=5 --num-calls=1
250
+ # httperf: warning: open file limit > FD_SETSIZE; limiting max. # of open files to FD_SETSIZE
251
+ # Maximum connect burst length: 1
252
+ #
253
+ # Total: connections 5 requests 5 replies 5 test-duration 0.513 s
254
+ #
255
+ # Connection rate: 9.7 conn/s (102.7 ms/conn, <=1 concurrent connections)
256
+ # Connection time [ms]: min 102.1 avg 102.7 max 104.1 median 102.5 stddev 0.8
257
+ # Connection time [ms]: connect 0.2
258
+ # Connection length [replies/conn]: 1.000
259
+ #
260
+ # Request rate: 9.7 req/s (102.7 ms/req)
261
+ # Request size [B]: 62.0
262
+ #
263
+ # Reply rate [replies/s]: min 0.0 avg 0.0 max 0.0 stddev 0.0 (0 samples)
264
+ # Reply time [ms]: response 102.3 transfer 0.1
265
+ # Reply size [B]: header 136.0 content 96.0 footer 0.0 (total 232.0)
266
+ # Reply status: 1xx=0 2xx=5 3xx=0 4xx=0 5xx=0
267
+ #
268
+ # CPU time [s]: user 0.12 system 0.39 (user 22.5% system 75.3% total 97.8%)
269
+ # Net I/O: 2.8 KB/s (0.0*10^6 bps)
270
+ #
271
+ # Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
272
+ # Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
273
+
274
+ def summary
275
+ return unless summary_file
276
+
277
+ raw = summary_file.readlines.join
278
+ stats = Stella::Test::Run::Summary.new
279
+
280
+ raw.scan(/Request rate: (\d+?\.\d+?) req.s .(\d+?\.\d+?) ms.req./) do |rate,time|
281
+ stats.transaction_rate = rate.to_f
282
+ stats.response_time = (time.to_f) / 1000
283
+ end
284
+
285
+ raw.scan(/connections (\d+?) requests (\d+?) replies (\d+?) test-duration (\d+\.\d+?) s/) do |conn,req,rep,time|
286
+ stats.elapsed_time = time.to_f
287
+ stats.successful = rep.to_i
288
+ stats.failed = conn.to_i - rep.to_i # maybe this should be from the Error line
289
+ stats.transactions = conn.to_i
290
+ end
291
+
292
+ raw.scan(/header (\d+\.\d+?)\s+.+?\s+.total (\d+\.\d+?)./) do |h,t|
293
+ stats.data_transferred = ((t.to_f || 0 ) / 1_048_576).to_f
294
+ stats.headers_transferred = ((h.to_f || 0 ) / 1_048_576).to_f
295
+ end
296
+ stats.vusers = self.vusers
297
+
298
+ stats
299
+ end
300
+
301
+
302
+
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,221 @@
1
+
2
+ require 'webrick'
3
+ require 'stringio'
4
+ require 'net/dns/packet'
5
+
6
+
7
+ module Stella
8
+ module Adapter
9
+ # Make sure Stella's lib directory is before the system defined ones.
10
+ # We are using a modified version of pcaplet.rb.
11
+ require 'pcap'
12
+ require 'observer'
13
+
14
+ # Stella::Adapter::PcapWatcher
15
+ #
16
+ # Record HTTP or DNS events with Pcap (TCP sniffer). This requires ruby-pcap and the C pcap
17
+ # library as well as root acceess (TCP packet sniffing requires root privileges). If you're
18
+ # running Ruby 1.9, JRuby, or Windows this will not be available on your system.
19
+ # To sniff traffic, you must be on either the machine sending the requests or the machine
20
+ # receiving the requests.
21
+ class PcapWatcher
22
+ include Observable
23
+
24
+ # Building Ruby::Pcap with Ruby 1.9.1
25
+ # RSTRING()->len ia now RSTRING_LEN(), ...
26
+ # see: http://gnufied.org/2007/12/21/mysql-c-bindings-for-ruby-19/#comment-3133
27
+ # see: http://www.rubyinside.com/ruby-1-9-1-preview-released-why-its-a-big-deal-1280.html#comment-37223
28
+ # TRAP_BEG and TRAP_END are also fucked. But the fix is not clear.
29
+ # Basically Ruby::PCap is not ready for 1.9
30
+ # See: http://d.hatena.ne.jp/takuma104/20080210/1202638583
31
+
32
+ # Network interface device ID. eri0, en0, lo0, etc... /sbin/ifconfig -a will tell you.
33
+ attr_accessor :device
34
+ # Buffer size, in bytes, to read from each packet (default: 1500)
35
+ attr_accessor :snaplen
36
+ # Port of the machine sending requests (default: 80)
37
+ attr_accessor :sport
38
+ # Port of the machine receiving requests (default: 80)
39
+ attr_accessor :dport
40
+ # dns or http
41
+ attr_accessor :service
42
+ # udp or tcp
43
+ attr_accessor :protocol
44
+ # Maximum number of packets to sniff
45
+ attr_accessor :maxpacks
46
+
47
+ attr_reader :pcaplet
48
+
49
+ def initialize(options={})
50
+ # The proper service name for dns is "domain"
51
+ @service = options[:service] || 'http'
52
+ @service = 'domain' if options[:service] == 'dns'
53
+
54
+ if @service == 'domain'
55
+ @protocol = 'udp'
56
+ else
57
+ @protocol = options[:protocol] || 'tcp'
58
+ end
59
+
60
+ @dport = options[:port] || Socket::getservbyname(@service)
61
+ @sport = options[:port] || @dport
62
+
63
+ @device = options[:device] || guess_device
64
+ @snaplen = options[:snaplen] || 10000 # 10KB
65
+ @maxpacks = options[:maxpacks] || 100_000
66
+
67
+ Stella::LOGGER.info("Watching interface #{@device} for #{@service} activity on #{@protocol} port #{@dport}")
68
+ end
69
+
70
+ def guess_device
71
+ # NOTE: This should be passed in as a value, not called from the global
72
+ case Stella::SYSINFO.implementation
73
+ when :osx
74
+ "en1" # Pcap.lookupdev returns en0
75
+ else
76
+ Pcap.lookupdev
77
+ end
78
+ end
79
+
80
+ def run
81
+
82
+ if (respond_to? "monitor_#{@service}")
83
+ self.send("monitor_#{@service}")
84
+ else
85
+ raise "Unknown service type (#{@service})"
86
+ end
87
+
88
+ end
89
+
90
+
91
+
92
+ # monitor_domain
93
+ #
94
+ # Use Ruby-Pcap to sniff packets off the network interface.
95
+ #
96
+ # DNS monitor based on: http://www.linuxjournal.com/article/9614
97
+ # Install http://rubyforge.org/projects/net-dns
98
+ #
99
+ # NOTE: Is there a better way to match up a request packet with a
100
+ # response packet?
101
+ # We keep connect a request with the response using the domain name.
102
+ # It's possible that two (or more) requests to be made for the same domain
103
+ # at the same time and the responses could be mixed up. This will affect
104
+ # the exact response time but probably not by much.
105
+ def monitor_domain
106
+
107
+ @pcaplet = Pcaplet.new(:device => @device, :count => @maxpacks)
108
+
109
+ req_filter = Pcap::Filter.new("#{@protocol} and dst port #{@dport}", @pcaplet.capture)
110
+ resp_filter = Pcap::Filter.new("#{@protocol} and src port #{@dport}", @pcaplet.capture)
111
+ @pcaplet.add_filter(req_filter | resp_filter)
112
+ @pcaplet.each_packet do |packet|
113
+ data = packet.udp_data
114
+ case packet
115
+ when req_filter
116
+ dobj = Stella::Data::DomainRequest.new(data)
117
+ dobj.time = packet.time
118
+ dobj.client_ip = packet.ip_src
119
+ dobj.server_ip = packet.ip_dst
120
+
121
+ changed
122
+ notify_observers(:domain_request, dobj)
123
+
124
+ when resp_filter
125
+ dobj = Stella::Data::DomainResponse.new(data)
126
+ dobj.time = packet.time
127
+ dobj.client_ip = packet.ip_dst
128
+ dobj.server_ip = packet.ip_src
129
+
130
+ changed
131
+ notify_observers(:domain_response, dobj)
132
+ end
133
+
134
+ end
135
+
136
+ rescue Interrupt
137
+ after
138
+ exit
139
+ end
140
+
141
+
142
+ def monitor_http
143
+
144
+ @pcaplet = Pcaplet.new(:device => @device, :count => @maxpacks)
145
+
146
+ begin
147
+ req_filter = Pcap::Filter.new("#{@protocol} and dst port #{@dport}", @pcaplet.capture)
148
+ resp_filter = Pcap::Filter.new("#{@protocol} and src port #{@sport}", @pcaplet.capture)
149
+ @pcaplet.add_filter(req_filter | resp_filter)
150
+ @pcaplet.each_packet do |packet|
151
+ data = packet.tcp_data
152
+ next if data.nil?
153
+
154
+
155
+ # NOTE: With HTTP 1.1 keep alive connections, multiple requests can be passed
156
+ # through single connection. This makes it difficult to match responses with
157
+ # requests.
158
+ # NOTE: We don't parse the body of POST and PUT requests because the data can
159
+ # be (and likely is), split across numerous packets. We also only grab 1500
160
+ # bytes from each packet.
161
+ # NOTE: The hostname is taken from the Host header. Requests made without
162
+ # this header (including HTTP 1.0) will contain the local hostname instead.
163
+ # TODO: resolve the hostname from the IP address.
164
+ # There are some helpful methods for doing some of this stuff:
165
+ # http://www.goto.info.waseda.ac.jp/~fukusima/ruby/pcap/doc/TCPPacket.html
166
+ case packet
167
+ when req_filter
168
+ next unless data and data =~ /^(GET|POST|HEAD|DELETE|PUT)\s+(.+?)\s+(HTTP.+?)$/
169
+ dobj = Stella::Data::HTTPRequest.new(data.gsub(/\r?\n/, $/)) # Use the system's line terminators
170
+ dobj.time = packet.time
171
+ dobj.client_ip = packet.ip_src
172
+ dobj.server_ip = packet.ip_dst
173
+
174
+ changed
175
+ notify_observers(:http_request, dobj)
176
+
177
+ when resp_filter
178
+ # NOTE: Some responses do not contain a body in the first packet.
179
+ # TODO: investigate further. Try: http://www.ruby-doc.org/core/classes/Enumerable.html
180
+ next unless data and data =~ /^(HTTP.+)$/
181
+ dobj = Stella::Data::HTTPResponse.new(data.gsub(/\r?\n/, $/))
182
+ dobj.time = packet.time
183
+ dobj.client_ip = packet.ip_dst
184
+ dobj.server_ip = packet.ip_src
185
+
186
+ changed
187
+ notify_observers(:http_response, dobj)
188
+
189
+ end
190
+
191
+ end
192
+ rescue Interrupt
193
+ after
194
+ exit
195
+ rescue => ex
196
+ Stella::LOGGER.error(ex)
197
+ end
198
+
199
+
200
+ end
201
+
202
+ def after
203
+ STDOUT.flush
204
+ stat = @pcaplet.capture.stats
205
+ if stat
206
+ Stella::LOGGER.info("#{$/}#{stat.recv} packets received by filter");
207
+ Stella::LOGGER.info("#{stat.drop} packets dropped by kernel", ''); # with an extra line
208
+ end
209
+ STDOUT.flush
210
+ @pcaplet.capture.close
211
+ delete_observers
212
+ rescue
213
+
214
+ # Ignore errors
215
+ end
216
+
217
+
218
+ end
219
+ end
220
+ end
221
+