drbdump 1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b3f2cd54ef03808d4d62ec58bbf6988a5a40657e
4
+ data.tar.gz: c83c65fb484a4aa329aa943b08c6b63f95061d68
5
+ SHA512:
6
+ metadata.gz: f8a8f622a06fdadd9495f13136bfc9c8acbb8a1e43d2a427c0cba184830324919f235916ecfb6249cb8637d7207ad7ef5c27fb29d6e19e7552a0cc39d2674af7
7
+ data.tar.gz: 8ff6002c16d643c7e88300ab91546595a247dc0e5b46a666c8f42bf08e2d89b6ad69f6991db645b4d8a0acb2e41674aba752065ac0b04fde9460ad83547d957c
Binary file
Binary file
@@ -0,0 +1,8 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ Autotest.add_hook :initialize do |at|
6
+ at.testlib = 'minitest/autorun'
7
+ end
8
+
File without changes
@@ -0,0 +1,4 @@
1
+ === 1.0 / 2013-05-31
2
+
3
+ * Birthday!
4
+
@@ -0,0 +1,30 @@
1
+ .autotest
2
+ History.rdoc
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ bin/drbdump
7
+ example/ping.rb
8
+ example/service_primes.rb
9
+ example/tuplespace_primes.rb
10
+ lib/drbdump.rb
11
+ lib/drbdump/loader.rb
12
+ lib/drbdump/message.rb
13
+ lib/drbdump/message_result.rb
14
+ lib/drbdump/message_send.rb
15
+ lib/drbdump/statistic.rb
16
+ lib/drbdump/statistics.rb
17
+ lib/drbdump/test_case.rb
18
+ test/arg.dump
19
+ test/drb_fin.dump
20
+ test/http.dump
21
+ test/ping.dump
22
+ test/ring.dump
23
+ test/test_drbdump.rb
24
+ test/test_drbdump_loader.rb
25
+ test/test_drbdump_message.rb
26
+ test/test_drbdump_message_result.rb
27
+ test/test_drbdump_message_send.rb
28
+ test/test_drbdump_statistic.rb
29
+ test/test_drbdump_statistics.rb
30
+ test/too_large_packet.pcap
@@ -0,0 +1,71 @@
1
+ = drbdump
2
+
3
+ home :: https://github.com/drbrain/drbdump
4
+ rdoc :: http://docs.seattlerb.org/drbdump
5
+ bugs :: https://github.com/drbrain/drbdump/issues
6
+
7
+ == Description
8
+
9
+ drbdump is a tcpdump-like tool for the dRuby protocol. It allows you to
10
+ inspect the message and analyze the messages sent between your DRb-using
11
+ processes.
12
+
13
+ == Features
14
+
15
+ * Displays DRb message sends and results
16
+ * Displays Rinda service discovery announcements
17
+ * Records DRb message statistics including allocations and latency
18
+
19
+ == Problems
20
+
21
+ * Does not handle out-of-order or resent TCP packets
22
+ * Does not handle mDNS
23
+ * Does not support filtering
24
+ * TCP only
25
+
26
+ == Usage
27
+
28
+ To show DRb messages:
29
+
30
+ sudo drbdump
31
+
32
+ See the DRbDump class documentation for a full description of drbdump
33
+
34
+ == Installation
35
+
36
+ sudo gem install drbdump
37
+
38
+ == Developers
39
+
40
+ After checking out the source, run:
41
+
42
+ $ rake newb
43
+
44
+ This task will install any missing dependencies, run the tests/specs,
45
+ and generate the RDoc.
46
+
47
+ == License
48
+
49
+ (The MIT License)
50
+
51
+ Copyright (c) Eric Hodel
52
+
53
+ Permission is hereby granted, free of charge, to any person obtaining
54
+ a copy of this software and associated documentation files (the
55
+ 'Software'), to deal in the Software without restriction, including
56
+ without limitation the rights to use, copy, modify, merge, publish,
57
+ distribute, sublicense, and/or sell copies of the Software, and to
58
+ permit persons to whom the Software is furnished to do so, subject to
59
+ the following conditions:
60
+
61
+ The above copyright notice and this permission notice shall be
62
+ included in all copies or substantial portions of the Software.
63
+
64
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
65
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
66
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
67
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
68
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
69
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
70
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
71
+
@@ -0,0 +1,30 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.plugin :git
7
+ Hoe.plugin :minitest
8
+ Hoe.plugin :travis
9
+
10
+ namespace :travis do
11
+ task :install_libpcap do
12
+ sh 'sudo apt-get install libpcap-dev'
13
+ end
14
+
15
+ task before: %w[install_libpcap]
16
+ end
17
+
18
+ Hoe.spec 'drbdump' do
19
+ developer 'Eric Hodel', 'drbrain@segment7.net'
20
+
21
+ rdoc_locations << 'docs.seattlerb.org:/data/www/docs.seattlerb.org/drbdump/'
22
+
23
+ self.readme_file = 'README.rdoc'
24
+ self.licenses << 'MIT'
25
+
26
+ self.extra_deps << ['capp', '~> 1.0']
27
+ self.extra_deps << ['marshal-structure', '~> 2.0']
28
+ end
29
+
30
+ # vim: syntax=ruby
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'drbdump'
4
+
5
+ DRbDump.run ARGV
6
+
@@ -0,0 +1,241 @@
1
+ require 'drb'
2
+ require 'drbdump'
3
+ require 'optparse'
4
+
5
+ class Ping
6
+
7
+ class Responder
8
+ def ping i, data
9
+ return i, data
10
+ end
11
+ end
12
+
13
+ def self.process_args argv
14
+ options = {
15
+ client: nil,
16
+ count: Float::INFINITY,
17
+ flood: false,
18
+ interval: 1,
19
+ reconnect: false,
20
+ server: false,
21
+ size: 0,
22
+ }
23
+
24
+ interval_set = false
25
+
26
+ op = OptionParser.new do |opt|
27
+ opt.banner = <<-BANNER
28
+ Usage: ping.rb [options] [druby://...]
29
+
30
+ With no arguments, spawns a child process and sends DRb messages to it
31
+
32
+ To ping across multiple machines start a server with:
33
+
34
+ ping.rb --server
35
+
36
+ And submit the given URI to a client:
37
+
38
+ ping.rb druby://...
39
+ BANNER
40
+
41
+ opt.separator nil
42
+ opt.separator 'Options:'
43
+
44
+ opt.on('-c', '--count COUNT', Integer,
45
+ 'Number of packets to send') do |count|
46
+ options[:count] = count
47
+ end
48
+
49
+ opt.on('-f', '--flood',
50
+ 'Send packets as fast as possible',
51
+ 'Prints one . per 1000 messages') do
52
+ options[:flood] = true
53
+ end
54
+
55
+ opt.on('-i', '--interval SECONDS', Float,
56
+ 'Time between non-flood packets') do |interval|
57
+ options[:interval] = interval
58
+ interval_set = true
59
+ end
60
+
61
+ opt.on('-s', '--packet-size SIZE', Integer,
62
+ 'Size of extra data to send') do |size|
63
+ options[:size] = size
64
+ end
65
+
66
+ opt.on( '--reconnect',
67
+ 'Reconnect for each ping') do
68
+ options[:reconnect] = true
69
+ end
70
+
71
+ opt.on( '--server',
72
+ 'Run only as a server') do |value|
73
+ options[:server] = true
74
+ end
75
+ end
76
+
77
+ op.parse! argv
78
+
79
+ raise OptionParser::ParseError, '--flood with --interval is nonsense' if
80
+ options[:flood] and interval_set
81
+
82
+ raise OptionParser::ParseError, '--server with --flood is nonsense' if
83
+ options[:server] and options[:flood]
84
+
85
+ raise OptionParser::ParseError, '--server with --count is nonsense' if
86
+ options[:server] and options[:count]
87
+
88
+ options[:client] = argv.shift if /\Adruby:/ =~ argv.first
89
+
90
+ raise OptionParser::ParseError, '--server with client URI is nonsense' if
91
+ options[:server] and options[:client]
92
+
93
+ options
94
+ rescue OptionParser::ParseError => e
95
+ $stderr.puts op
96
+ $stderr.puts
97
+ $stderr.puts e.message
98
+
99
+ abort
100
+ end
101
+
102
+ def self.run argv = ARGV
103
+ options = process_args argv
104
+
105
+ ping = new options
106
+
107
+ ping.run
108
+ end
109
+
110
+ def initialize options
111
+ @count = options[:count]
112
+ @client = options[:client]
113
+ @flood = options[:flood]
114
+ @interval = options[:interval]
115
+ @reconnect = options[:reconnect]
116
+ @remote = nil
117
+ @server = options[:server]
118
+ @size = options[:size]
119
+ @uri = nil
120
+
121
+ @data = ('a'..'z').cycle.first(@size).join
122
+ end
123
+
124
+ def delay_ping
125
+ statistic = DRbDump::Statistic.new
126
+ seq = 0
127
+
128
+ until (seq += 1) > @count do
129
+ send_message seq, statistic
130
+ end
131
+ ensure
132
+ puts
133
+ puts delay_statistics statistic
134
+ end
135
+
136
+ def delay_statistics statistic
137
+ '%d messages, min/avg/max/stddev = %0.3f/%0.3f/%0.3f/%0.3f ms' % [
138
+ statistic.count, statistic.min, statistic.mean, statistic.max,
139
+ statistic.standard_deviation
140
+ ]
141
+ end
142
+
143
+ def flood_ping
144
+ start = Time.now
145
+ seq = 0
146
+
147
+ until (seq += 1) > @count do
148
+ begin
149
+ @remote.ping seq, @data
150
+ rescue DRb::DRbConnError
151
+ end
152
+
153
+ reconnect
154
+
155
+ print '.' if seq % 1000 == 0
156
+ end
157
+ ensure
158
+ elapsed = Time.now - start
159
+ puts
160
+ puts flood_statistics(elapsed, seq - 1)
161
+ end
162
+
163
+ def flood_statistics elapsed, messages
164
+ '%d messages in %0.3f seconds, %d messages/sec' % [
165
+ messages, elapsed, messages / elapsed
166
+ ]
167
+ end
168
+
169
+ def loopback
170
+ uri = DRb.uri
171
+
172
+ pid = fork do
173
+ DRb.stop_service
174
+
175
+ DRb.start_service
176
+
177
+ ping uri
178
+ end
179
+
180
+ trap 'INT' do Process.kill 'INT', pid end
181
+ trap 'TERM' do Process.kill 'TERM', pid end
182
+
183
+ Process.wait pid
184
+ end
185
+
186
+ def ping uri
187
+ @uri = uri
188
+ @remote = DRb::DRbObject.new_with_uri @uri
189
+
190
+ if @flood then
191
+ flood_ping
192
+ else
193
+ delay_ping
194
+ end
195
+ end
196
+
197
+ def reconnect
198
+ return unless @reconnect
199
+
200
+ DRb::DRbConn.open @uri do [false, false] end
201
+ end
202
+
203
+ def run
204
+ DRb.start_service nil, Responder.new
205
+
206
+ if @server then
207
+ puts DRb.uri
208
+
209
+ DRb.thread.join
210
+ elsif @client then
211
+ ping @client
212
+ else
213
+ loopback
214
+ end
215
+ end
216
+
217
+ def send_message seq, statistic
218
+ message = 'success'
219
+ start = Time.now
220
+
221
+ begin
222
+ @remote.ping seq, @data
223
+ rescue DRb::DRbConnError => e
224
+ message = e
225
+ end
226
+
227
+ elapsed = (Time.now - start) * 1000
228
+
229
+ statistic.add elapsed
230
+
231
+ puts '%s %s: seq=%d time=%0.3f ms' % [@uri, message, seq, elapsed]
232
+
233
+ reconnect
234
+
235
+ sleep @interval
236
+ end
237
+
238
+ end
239
+
240
+ Ping.run ARGV if $0 == __FILE__
241
+
@@ -0,0 +1,212 @@
1
+ require 'drb'
2
+ require 'observer'
3
+ require 'thread'
4
+
5
+ ##
6
+ # Generates primes using multiple independent services. This prime number
7
+ # generator is purposefully ridiculous in its separation of services.
8
+ #
9
+ # == Implementation Notes
10
+ #
11
+ # ServicePrimes contains the following services:
12
+ #
13
+ # :primes::
14
+ # NumberStore holding prime numbers and Observable for notification of newly
15
+ # added prime numbers.
16
+ # :candidates::
17
+ # Enumerator from 3 to infinity for candidates.
18
+ # :discover::
19
+ # Consumes an item from the :candidates service and adds the number to the
20
+ # :primes service if it is found to be prime.
21
+ # :show_primes::
22
+ # Observer for the :primes NumberStore that prints added primes to standard
23
+ # output
24
+ #
25
+ # ServicePrimes can be collapsed back to a single process by removing
26
+ # fork_child and assigning each service to the instance variable used by each
27
+ # service.
28
+ #
29
+ # == Analyzing with drbdump
30
+ #
31
+ # Setup and finding the first prime (3) occurs within the first 15 messages.
32
+ #
33
+ # Comparing short message capture (100 messages) at the beginning of prime
34
+ # generation followed by later (around 70001) shows test_prime doing more work
35
+ # as the number of +call+ messages becomes a larger fraction of the messages
36
+ # sent.
37
+ #
38
+ # Removing DrbUndumped from NumberStore and NumberShower gives an interesting
39
+ # failure where the Observer fails to register.
40
+
41
+ class ServicePrimes
42
+
43
+ ##
44
+ # An observer for NumberStore that prints numbers as they are added.
45
+
46
+ class NumberShower
47
+ include DRb::DRbUndumped
48
+
49
+ ##
50
+ # Prints the number to standard output
51
+
52
+ def update number
53
+ puts number
54
+ end
55
+ end
56
+
57
+ ##
58
+ # Stores primes and allows access to generated primes. The NumberStore is
59
+ # pre-primed with the first prime number (2). The NumberStore is Observable
60
+ # and will notify observers when a new prime is added.
61
+
62
+ class NumberStore
63
+ include DRb::DRbUndumped
64
+ include Enumerable
65
+ include Observable
66
+
67
+ def initialize # :nodoc:
68
+ @primes = [2]
69
+ end
70
+
71
+ ##
72
+ # Adds a number +value+ and notifies observers
73
+
74
+ def add value
75
+ changed
76
+ @primes << value
77
+ notify_observers value
78
+ end
79
+
80
+ ##
81
+ # Iterates through stored primes
82
+
83
+ def each
84
+ @primes.each do |prime|
85
+ yield prime
86
+ end
87
+ end
88
+ end
89
+
90
+ def initialize # :nodoc:
91
+ @services = {}
92
+
93
+ DRb.start_service nil, @services
94
+
95
+ @uri = DRb.uri
96
+ end
97
+
98
+ ##
99
+ # Creates a candidate numbers service which is enumerator of integers from 3
100
+ # to infinity.
101
+
102
+ def candidates
103
+ fork_child :candidates do
104
+ @services[:candidates] = 3.upto Float::INFINITY
105
+
106
+ DRb.thread.join
107
+ end
108
+ end
109
+
110
+ ##
111
+ # Creates a prime number discovery service which consumes a candidate number
112
+ # and uses +test_prime+ to determine if the candidate number is prime. If
113
+ # the candidate is prime it is added to the prime number list.
114
+
115
+ def discover
116
+ fork_child :discover do
117
+ candidates = @services[:candidates]
118
+ @primes = @services[:primes]
119
+
120
+ candidates.each do |candidate|
121
+ prime = test_prime candidate
122
+
123
+ @primes.add candidate if prime
124
+ end
125
+ end
126
+ end
127
+
128
+ ##
129
+ # Forks a worker child named +service+ and waits for its registration before
130
+ # returning.
131
+
132
+ def fork_child service
133
+ Thread.start do
134
+ pid = fork do
135
+ DRb.stop_service
136
+
137
+ DRb.start_service
138
+
139
+ @services = DRb::DRbObject.new_with_uri @uri
140
+
141
+ yield
142
+ end
143
+
144
+ Process.wait pid
145
+ end
146
+
147
+ Thread.pass until @services[service]
148
+ end
149
+
150
+ ##
151
+ # Creates a prime number storage service named primes.
152
+
153
+ def primes
154
+ fork_child :primes do
155
+ @services[:primes] = NumberStore.new
156
+
157
+ DRb.thread.join
158
+ end
159
+ end
160
+
161
+ ##
162
+ # Starts the prime number generator
163
+
164
+ def run
165
+ candidates
166
+
167
+ primes
168
+
169
+ show_primes
170
+
171
+ discover
172
+
173
+ DRb.thread.join
174
+ end
175
+
176
+ ##
177
+ # Creates a prime number display service that observes the addition of new
178
+ # prime numbers to the primes service.
179
+
180
+ def show_primes
181
+ fork_child :show_primes do
182
+ prime_shower = NumberShower.new
183
+
184
+ @services[:primes].add_observer prime_shower
185
+
186
+ @services[:show_primes] = prime_shower
187
+
188
+ DRb.thread.join
189
+ end
190
+ end
191
+
192
+ ##
193
+ # Determines if +candidate+ is a prime number by dividing it by all primes
194
+ # below the square root of the +candidate+.
195
+
196
+ def test_prime candidate
197
+ max = Math.sqrt(candidate).ceil
198
+
199
+ is_prime = @primes.each do |prime|
200
+ break true if prime > max
201
+
202
+ _, remainder = candidate.divmod prime
203
+
204
+ break false if remainder.zero?
205
+ end
206
+
207
+ is_prime
208
+ end
209
+
210
+ end
211
+
212
+ ServicePrimes.new.run if $0 == __FILE__