ruby-breakpoint 0.5.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.
data/README ADDED
@@ -0,0 +1,43 @@
1
+ = ruby-breakpoint 0.5.0 README
2
+
3
+ ruby-breakpoint lets you inspect and modify state at run time. This allows you
4
+ to diagnose bugs, patch
5
  applications and more all via IRB by simply doing a
6
+ method call at the place you want to investigate.
7
+
8
+ ruby-breakpoint is integrated into the popular RubyOnRails and Nitro web
9
+ development frameworks. In the past it has also been a part of the Ruby
10
+ dev-utils Project.
11
+
12
+ ruby-breakpoint is available from http://ruby-breakpoint.rubyforge.org/ --
13
+ that web site also is a good place for discussing new features, filing bug
14
+ reports and so on.
15
+
16
+ See the NEWS file for the changes that were done in this release.
17
+
18
+
19
+ == Requirements
20
+
21
+ * Ruby >= 1.8.2
22
+ * DRb, IRB (usually shipped with Ruby, but not on Debian)
23
+
24
+
25
+ == Installation
26
+
27
+ De-compress archive and enter its top directory. Then type:
28
+
29
+ ($ su)
30
+ # ruby setup.rb
31
+
32
+ These simple step installs this program under the default location of Ruby
33
+ libraries. You can also install files into your favorite directory by supplying
34
+ setup.rb some options. Try "ruby setup.rb --help".
35
+
36
+
37
+ == License
38
+
39
+ Dual licensed under GPL and Ruby's custom license. See COPYING.
40
+
41
+
42
+ == Author
43
+
44
+ Florian Gross, flgr@ccan.de
data/TODO ADDED
@@ -0,0 +1,31 @@
1
+ = TODO
2
+
3
+ * Factor out remote breakpoint support to a separate file so it can be loaded
4
+ on-demand. This would allow the local breakpoint functionality to work even
5
+ when DRb is not available. (E.g. on Debian)
6
+
7
+ = Done
8
+
9
+ * Do further research on the write SecurityError.
10
+ See ruby-core for more information.
11
+ * This seems to have been a bug in Ruby 1.8.1 and early snapshots in Ruby 1.8.2.
12
+ The problem can be fixed by updating to a more current Ruby. More information
13
+ regarding this is available at the Wiki:
14
+
15
+ http://ruby-breakpoint.rubyforge.org/wiki/wiki.pl?FrequentlyAskedQuestions
16
+
17
+ * Find out whether we need to add Windows and Linux wrappers to the bin/ directory
18
+ * This is handled correctly by RubyGems. setup.rb only copies the .rb file to a
19
+ suitable directory, but that should work in the usual case.
20
+
21
+ * breakpoint_client needs logic for detecting the default client uri based on the
22
+ server uri:
23
+ * drbunix:/foo/bar => drbunix:#{File.join(Dir.tmpdir, "#{random}.breakpoint")}
24
+ (require 'tmpdir')
25
+ * druby://localhost:port => druby://localhost:#{rand_port}
26
+
27
+ * Mention available ressources (especially the FAQ) in breakpoint_client --help
28
+
29
+ * Document drbunix:/ uris and how they are more secure than druby:// with ACLs.
30
+ Suggest that this be used in environments where there are lots of users sharing
31
+ the same system. (E.g. web hosters)
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'breakpoint'
4
+ require 'optparse'
5
+ require 'timeout'
6
+ require 'tmpdir'
7
+
8
+ Options = {
9
+ :ClientURI => nil,
10
+ :ServerURI => "druby://localhost:42531",
11
+ :RetryDelay => 3,
12
+ :Permanent => false,
13
+ :Verbose => false
14
+ }
15
+
16
+ ARGV.options do |opts|
17
+ script_name = File.basename($0)
18
+ opts.banner = [
19
+ "Usage: ruby #{script_name} [Options] [server uri]",
20
+ "",
21
+ "This tool lets you connect to a breakpoint service ",
22
+ "which was started via Breakpoint.activate_drb.",
23
+ "",
24
+ "The server uri defaults to druby://localhost:42531",
25
+ "",
26
+ "Having trouble or need help?",
27
+ "* Homepage: http://ruby-breakpoint.rubyforge.org/ (has FAQ!)",
28
+ "* Author: Florian Gross, flgr@ccan.de (Read homepage first!)"
29
+ ].join("\n")
30
+
31
+ opts.separator ""
32
+
33
+ opts.on("-c", "--client-uri=uri",
34
+ "Run the client on the specified uri.",
35
+ "This can be used to specify the port",
36
+ "that the client uses to allow for back",
37
+ "connections from the server.",
38
+ "Default: Find a good URI automatically.",
39
+ "Example: -c druby://localhost:12345"
40
+ ) { |Options[:ClientURI]| }
41
+
42
+ opts.on("-s", "--server-uri=uri",
43
+ "Connect to the server specified at the",
44
+ "specified uri.",
45
+ "Default: druby://localhost:42531"
46
+ ) { |Options[:ServerURI]| }
47
+
48
+ opts.on("-R", "--retry-delay=delay", Integer,
49
+ "Automatically try to reconnect to the",
50
+ "server after delay seconds when the",
51
+ "connection failed or timed out.",
52
+ "A value of 0 disables automatical",
53
+ "reconnecting completely.",
54
+ "Default: 10"
55
+ ) { |Options[:RetryDelay]| }
56
+
57
+ opts.on("-P", "--[no-]permanent",
58
+ "Run the breakpoint client in permanent mode.",
59
+ "This means that the client will keep continue",
60
+ "running even after the server has closed the",
61
+ "connection. Useful for example in Rails.",
62
+ "Default: non-permanent"
63
+ ) { |Options[:Permanent]| }
64
+
65
+ opts.on("-V", "--[no-]verbose",
66
+ "Run the breakpoint client in verbose mode.",
67
+ "Will produce more messages, for example between",
68
+ "individual breakpoints. This might help in seeing",
69
+ "that the breakpoint client is still alive, but adds",
70
+ "quite a bit of clutter.",
71
+ "Default: non-verbose"
72
+ ) { |Options[:Verbose]| }
73
+
74
+ opts.separator ""
75
+
76
+ opts.on("-h", "--help",
77
+ "Show this help message."
78
+ ) { puts opts; exit }
79
+ opts.on("-v", "--version",
80
+ "Display the version information."
81
+ ) do
82
+ id = %q$Id: breakpoint_client 50 2005-02-26 19:31:51Z flgr $
83
+ puts id.sub("Id: ", "")
84
+ puts "(Breakpoint::Version = #{Breakpoint::Version})"
85
+ exit
86
+ end
87
+
88
+ opts.parse!
89
+ end
90
+
91
+ Options[:ServerURI] = ARGV[0] if ARGV[0]
92
+ Options[:ClientURI] ||= case Options[:ServerURI]
93
+ when /^drbunix:(.+)$/i then
94
+ "drbunix:" << File.join(Dir.tmpdir, $1.gsub(/\W/, "_")) << ".breakpoint_client"
95
+ when %r{^druby://(localhost|127\.0\.0\.1|::1):(\d+)$}i then
96
+ "druby://" << $1 << ":" << $2.succ
97
+ end
98
+
99
+ puts "ClientURI is #{Options[:ClientURI] || "unspecified"}" if Options[:Verbose]
100
+
101
+ module Handlers
102
+ extend self
103
+
104
+ def breakpoint_handler(workspace, message)
105
+ puts message
106
+ IRB.start(nil, nil, workspace)
107
+
108
+ puts ""
109
+ if Options[:Verbose] then
110
+ puts "Resumed execution. Waiting for next breakpoint...", ""
111
+ end
112
+ end
113
+
114
+ def eval_handler(code)
115
+ result = eval(code, TOPLEVEL_BINDING)
116
+ if result then
117
+ DRbObject.new(result)
118
+ else
119
+ result
120
+ end
121
+ end
122
+
123
+ def collision_handler()
124
+ msg = [
125
+ " *** Breakpoint service collision ***",
126
+ " Another Breakpoint service tried to use the",
127
+ " port already occupied by this one. It will",
128
+ " keep waiting until this Breakpoint service",
129
+ " is shut down.",
130
+ " ",
131
+ " If you are using the Breakpoint library for",
132
+ " debugging a Rails or other CGI application",
133
+ " this likely means that this Breakpoint",
134
+ " session belongs to an earlier, outdated",
135
+ " request and should be shut down via 'exit'."
136
+ ].join("\n")
137
+
138
+ if RUBY_PLATFORM["win"] then
139
+ # This sucks. Sorry, I'm not doing this because
140
+ # I like funky message boxes -- I need to do this
141
+ # because on Windows I have no way of displaying
142
+ # my notification via puts() when gets() is still
143
+ # being performed on STDIN. I have not found a
144
+ # better solution.
145
+ begin
146
+ require 'tk'
147
+ root = TkRoot.new { withdraw }
148
+ Tk.messageBox('message' => msg, 'type' => 'ok')
149
+ root.destroy
150
+ rescue Exception
151
+ puts "", msg, ""
152
+ end
153
+ else
154
+ puts "", msg, ""
155
+ end
156
+ end
157
+ end
158
+
159
+ # Used for checking whether we are currently in the reconnecting loop.
160
+ reconnecting = false
161
+
162
+ loop do
163
+ DRb.start_service(Options[:ClientURI])
164
+
165
+ begin
166
+ service = DRbObject.new(nil, Options[:ServerURI])
167
+
168
+ begin
169
+ ehandler = Handlers.method(:eval_handler)
170
+ chandler = Handlers.method(:collision_handler)
171
+ handler = Handlers.method(:breakpoint_handler)
172
+ service.eval_handler = ehandler
173
+ service.collision_handler = chandler
174
+ service.handler = handler
175
+
176
+ reconnecting = false
177
+ if Options[:Verbose] then
178
+ puts "Connection established. Waiting for breakpoint...", ""
179
+ end
180
+
181
+ loop do
182
+ begin
183
+ service.ping
184
+ rescue DRb::DRbConnError => error
185
+ puts "Server exited. Closing connection...", ""
186
+ DRb.stop_service
187
+ exit! unless Options[:Permanent]
188
+ break
189
+ end
190
+
191
+ sleep(0.5)
192
+ end
193
+ ensure
194
+ service.eval_handler = nil
195
+ service.collision_handler = nil
196
+ service.handler = nil
197
+ end
198
+ rescue Exception => error
199
+ if Options[:RetryDelay] > 0 then
200
+ if not reconnecting then
201
+ reconnecting = true
202
+ puts "No connection to breakpoint service at #{Options[:ServerURI]} " +
203
+ "(#{error.class})"
204
+ puts error.backtrace if $DEBUG
205
+ puts "Tries to connect will be made every #{Options[:RetryDelay]} seconds..."
206
+ end
207
+
208
+ sleep Options[:RetryDelay]
209
+ retry
210
+ else
211
+ raise
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,221 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!DOCTYPE html
3
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
+
6
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
7
+ <head>
8
+ <title>Class: Binding</title>
9
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
+ <meta http-equiv="Content-Script-Type" content="text/javascript" />
11
+ <link rel="stylesheet" href=".././rdoc-style.css" type="text/css" media="screen" />
12
+ <script type="text/javascript">
13
+ // <![CDATA[
14
+
15
+ function popupCode( url ) {
16
+ window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
17
+ }
18
+
19
+ function toggleCode( id ) {
20
+ if ( document.getElementById )
21
+ elem = document.getElementById( id );
22
+ else if ( document.all )
23
+ elem = eval( "document.all." + id );
24
+ else
25
+ return false;
26
+
27
+ elemStyle = elem.style;
28
+
29
+ if ( elemStyle.display != "block" ) {
30
+ elemStyle.display = "block"
31
+ } else {
32
+ elemStyle.display = "none"
33
+ }
34
+
35
+ return true;
36
+ }
37
+
38
+ // Make codeblocks hidden by default
39
+ document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
40
+
41
+ // ]]>
42
+ </script>
43
+
44
+ </head>
45
+ <body>
46
+
47
+
48
+
49
+ <div id="classHeader">
50
+ <table class="header-table">
51
+ <tr class="top-aligned-row">
52
+ <td><strong>Class</strong></td>
53
+ <td class="class-name-in-header">Binding</td>
54
+ </tr>
55
+ <tr class="top-aligned-row">
56
+ <td><strong>In:</strong></td>
57
+ <td>
58
+ <a href="../files/lib/binding_of_caller_rb.html">
59
+ lib/binding_of_caller.rb
60
+ </a>
61
+ <br />
62
+ </td>
63
+ </tr>
64
+
65
+ <tr class="top-aligned-row">
66
+ <td><strong>Parent:</strong></td>
67
+ <td>
68
+ Object
69
+ </td>
70
+ </tr>
71
+ </table>
72
+ </div>
73
+ <!-- banner header -->
74
+
75
+ <div id="bodyContent">
76
+
77
+
78
+
79
+ <div id="contextContent">
80
+
81
+
82
+
83
+ </div>
84
+
85
+ <div id="method-list">
86
+ <h3 class="section-bar">Methods</h3>
87
+
88
+ <div class="name-list">
89
+ <a href="#M000003">of_caller</a>&nbsp;&nbsp;
90
+ </div>
91
+ </div>
92
+
93
+ </div>
94
+
95
+
96
+ <!-- if includes -->
97
+
98
+ <div id="section">
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+ <!-- if method_list -->
108
+ <div id="methods">
109
+ <h3 class="section-bar">Public Class methods</h3>
110
+
111
+ <div id="method-M000003" class="method-detail">
112
+ <a name="M000003"></a>
113
+
114
+ <div class="method-heading">
115
+ <a href="#M000003" class="method-signature">
116
+ <span class="method-name">of_caller</span><span class="method-args">() {|result| ...}</span>
117
+ </a>
118
+ </div>
119
+
120
+ <div class="method-description">
121
+ <p>
122
+ This method returns the binding of the method that called your method. It
123
+ will raise an Exception when you&#8217;re not inside a method.
124
+ </p>
125
+ <p>
126
+ It&#8217;s used like this:
127
+ </p>
128
+ <pre>
129
+ def inc_counter(amount = 1)
130
+ Binding.of_caller do |binding|
131
+ # Create a lambda that will increase the variable 'counter'
132
+ # in the caller of this method when called.
133
+ inc = eval(&quot;lambda { |arg| counter += arg }&quot;, binding)
134
+ # We can refer to amount from inside this block safely.
135
+ inc.call(amount)
136
+ end
137
+ # No other statements can go here. Put them inside the block.
138
+ end
139
+ counter = 0
140
+ 2.times { inc_counter }
141
+ counter # =&gt; 2
142
+ </pre>
143
+ <p>
144
+ <a href="Binding.html#M000003">Binding.of_caller</a> must be the last
145
+ statement in the method. This means that you will have to put everything
146
+ you want to do after the call to <a
147
+ href="Binding.html#M000003">Binding.of_caller</a> into the block of it.
148
+ This should be no problem however, because Ruby has closures. If you
149
+ don&#8217;t do this an Exception will be raised. Because of the way that <a
150
+ href="Binding.html#M000003">Binding.of_caller</a> is implemented it has to
151
+ be done this way.
152
+ </p>
153
+ <p><a class="source-toggle" href="#"
154
+ onclick="toggleCode('M000003-source');return false;">[Source]</a></p>
155
+ <div class="method-source-code" id="M000003-source">
156
+ <pre>
157
+ <span class="ruby-comment cmt"># File lib/binding_of_caller.rb, line 35</span>
158
+ 35: <span class="ruby-keyword kw">def</span> <span class="ruby-constant">Binding</span>.<span class="ruby-identifier">of_caller</span>(<span class="ruby-operator">&amp;</span><span class="ruby-identifier">block</span>)
159
+ 36: <span class="ruby-identifier">old_critical</span> = <span class="ruby-constant">Thread</span>.<span class="ruby-identifier">critical</span>
160
+ 37: <span class="ruby-constant">Thread</span>.<span class="ruby-identifier">critical</span> = <span class="ruby-keyword kw">true</span>
161
+ 38: <span class="ruby-identifier">count</span> = <span class="ruby-value">0</span>
162
+ 39: <span class="ruby-identifier">cc</span>, <span class="ruby-identifier">result</span>, <span class="ruby-identifier">error</span>, <span class="ruby-identifier">extra_data</span> = <span class="ruby-constant">Continuation</span>.<span class="ruby-identifier">create</span>(<span class="ruby-keyword kw">nil</span>, <span class="ruby-keyword kw">nil</span>)
163
+ 40: <span class="ruby-identifier">error</span>.<span class="ruby-identifier">call</span> <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">error</span>
164
+ 41:
165
+ 42: <span class="ruby-identifier">tracer</span> = <span class="ruby-identifier">lambda</span> <span class="ruby-keyword kw">do</span> <span class="ruby-operator">|</span><span class="ruby-operator">*</span><span class="ruby-identifier">args</span><span class="ruby-operator">|</span>
166
+ 43: <span class="ruby-identifier">type</span>, <span class="ruby-identifier">context</span>, <span class="ruby-identifier">extra_data</span> = <span class="ruby-identifier">args</span>[<span class="ruby-value">0</span>], <span class="ruby-identifier">args</span>[<span class="ruby-value">4</span>], <span class="ruby-identifier">args</span>
167
+ 44: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">type</span> <span class="ruby-operator">==</span> <span class="ruby-value str">&quot;return&quot;</span>
168
+ 45: <span class="ruby-identifier">count</span> <span class="ruby-operator">+=</span> <span class="ruby-value">1</span>
169
+ 46: <span class="ruby-comment cmt"># First this method and then calling one will return --</span>
170
+ 47: <span class="ruby-comment cmt"># the trace event of the second event gets the context</span>
171
+ 48: <span class="ruby-comment cmt"># of the method which called the method that called this</span>
172
+ 49: <span class="ruby-comment cmt"># method.</span>
173
+ 50: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">count</span> <span class="ruby-operator">==</span> <span class="ruby-value">2</span>
174
+ 51: <span class="ruby-comment cmt"># It would be nice if we could restore the trace_func</span>
175
+ 52: <span class="ruby-comment cmt"># that was set before we swapped in our own one, but</span>
176
+ 53: <span class="ruby-comment cmt"># this is impossible without overloading set_trace_func</span>
177
+ 54: <span class="ruby-comment cmt"># in current Ruby.</span>
178
+ 55: <span class="ruby-identifier">set_trace_func</span>(<span class="ruby-keyword kw">nil</span>)
179
+ 56: <span class="ruby-identifier">cc</span>.<span class="ruby-identifier">call</span>(<span class="ruby-identifier">eval</span>(<span class="ruby-value str">&quot;binding&quot;</span>, <span class="ruby-identifier">context</span>), <span class="ruby-keyword kw">nil</span>, <span class="ruby-identifier">extra_data</span>)
180
+ 57: <span class="ruby-keyword kw">end</span>
181
+ 58: <span class="ruby-keyword kw">elsif</span> <span class="ruby-identifier">type</span> <span class="ruby-operator">==</span> <span class="ruby-value str">&quot;line&quot;</span> <span class="ruby-keyword kw">then</span>
182
+ 59: <span class="ruby-keyword kw">nil</span>
183
+ 60: <span class="ruby-keyword kw">elsif</span> <span class="ruby-identifier">type</span> <span class="ruby-operator">==</span> <span class="ruby-value str">&quot;c-return&quot;</span> <span class="ruby-keyword kw">and</span> <span class="ruby-identifier">extra_data</span>[<span class="ruby-value">3</span>] <span class="ruby-operator">==</span> <span class="ruby-identifier">:set_trace_func</span> <span class="ruby-keyword kw">then</span>
184
+ 61: <span class="ruby-keyword kw">nil</span>
185
+ 62: <span class="ruby-keyword kw">else</span>
186
+ 63: <span class="ruby-identifier">set_trace_func</span>(<span class="ruby-keyword kw">nil</span>)
187
+ 64: <span class="ruby-identifier">error_msg</span> = <span class="ruby-value str">&quot;Binding.of_caller used in non-method context or &quot;</span> <span class="ruby-operator">+</span>
188
+ 65: <span class="ruby-value str">&quot;trailing statements of method using it aren't in the block.&quot;</span>
189
+ 66: <span class="ruby-identifier">cc</span>.<span class="ruby-identifier">call</span>(<span class="ruby-keyword kw">nil</span>, <span class="ruby-identifier">lambda</span> { <span class="ruby-identifier">raise</span>(<span class="ruby-constant">ArgumentError</span>, <span class="ruby-identifier">error_msg</span>) }, <span class="ruby-keyword kw">nil</span>)
190
+ 67: <span class="ruby-keyword kw">end</span>
191
+ 68: <span class="ruby-keyword kw">end</span>
192
+ 69:
193
+ 70: <span class="ruby-keyword kw">unless</span> <span class="ruby-identifier">result</span>
194
+ 71: <span class="ruby-identifier">set_trace_func</span>(<span class="ruby-identifier">tracer</span>)
195
+ 72: <span class="ruby-keyword kw">return</span> <span class="ruby-keyword kw">nil</span>
196
+ 73: <span class="ruby-keyword kw">else</span>
197
+ 74: <span class="ruby-constant">Thread</span>.<span class="ruby-identifier">critical</span> = <span class="ruby-identifier">old_critical</span>
198
+ 75: <span class="ruby-keyword kw">case</span> <span class="ruby-identifier">block</span>.<span class="ruby-identifier">arity</span>
199
+ 76: <span class="ruby-keyword kw">when</span> <span class="ruby-value">1</span> <span class="ruby-keyword kw">then</span> <span class="ruby-keyword kw">yield</span>(<span class="ruby-identifier">result</span>)
200
+ 77: <span class="ruby-keyword kw">else</span> <span class="ruby-keyword kw">yield</span>(<span class="ruby-identifier">result</span>, <span class="ruby-identifier">extra_data</span>)
201
+ 78: <span class="ruby-keyword kw">end</span>
202
+ 79: <span class="ruby-keyword kw">end</span>
203
+ 80: <span class="ruby-keyword kw">end</span>
204
+ </pre>
205
+ </div>
206
+ </div>
207
+ </div>
208
+
209
+
210
+ </div>
211
+
212
+
213
+ </div>
214
+
215
+
216
+ <div id="validator-badges">
217
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
218
+ </div>
219
+
220
+ </body>
221
+ </html>