ladybug 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -7
- data/lib/ladybug/debugger.rb +30 -10
- data/lib/ladybug/middleware.rb +43 -35
- data/lib/ladybug/object_manager.rb +1 -3
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b73479c1e9523e9a0f5798daed1707ed24baf74c
|
4
|
+
data.tar.gz: d046e7c87d246a2e3a156ad1bf71c23fc256789c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c79d7443d7cebedbb3174fe5a340aa21d2c23d201df2fab87aa6294326c839b3371e187f46eb7d351f4c7c8ad8bf103197882ded9dfb588284f1ae3422eca4ed
|
7
|
+
data.tar.gz: 58f696d4d5dc454c68215c0a3780f9ea2c45d070db1e168f915f2be26bf7ec5e567a02c110439291962f8db88f4e481a4d70978e669f0d7f01c0129c8f8893b4
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ Chrome Devtools as a user interface.
|
|
6
6
|
It aims to provide a rich backend debugging experience in a UI that many
|
7
7
|
web developers are already familiar with for debugging frontend Javascript.
|
8
8
|
|
9
|
-
**This project is currently in
|
9
|
+
**This project is currently in an early experimental phase.** Expect many limitations and bugs. If you try it out, please file
|
10
10
|
Github issues or [email me](mailto:gklitt@gmail.com) to help make this a
|
11
11
|
more useful tool.
|
12
12
|
|
@@ -14,9 +14,9 @@ more useful tool.
|
|
14
14
|
|
15
15
|
## Get started
|
16
16
|
|
17
|
-
1) Install the gem:
|
17
|
+
1) Install the gem, or add it to your Gemfile:
|
18
18
|
|
19
|
-
`gem install
|
19
|
+
`gem install ladybug`
|
20
20
|
|
21
21
|
2) ladybug is implemented as a Rack middleware, so you'll need to add
|
22
22
|
`Ladybug::Middleware` to the Rack middleware stack.
|
@@ -24,7 +24,7 @@ For example, in Rails 5, add
|
|
24
24
|
the following line to `config/application.rb`:
|
25
25
|
|
26
26
|
```
|
27
|
-
config.middleware.
|
27
|
+
config.middleware.use(Ladybug::Middleware)
|
28
28
|
```
|
29
29
|
|
30
30
|
3) Make sure you're using the puma web server, which is currently the
|
@@ -41,7 +41,11 @@ You should see your server program output something like:
|
|
41
41
|
In the Sources tab, you can view your Ruby source code.
|
42
42
|
If you set a breakpoint and then make another request to your server,
|
43
43
|
it should pause on the breakpoint and you'll be able to inspect
|
44
|
-
|
44
|
+
local and instance variables in Devtools.
|
45
|
+
|
46
|
+
6) You can then use the "step over" button to step through your code,
|
47
|
+
or "continue" to continue code execution. "Step into" and "Step out"
|
48
|
+
also work in some contexts.
|
45
49
|
|
46
50
|
**Security warning:** This debugger should only be run in local development.
|
47
51
|
Running it on a server open to the internet could allow anyone to
|
@@ -49,11 +53,10 @@ execute code on your server without authenticating.
|
|
49
53
|
|
50
54
|
## Development status
|
51
55
|
|
52
|
-
* basic pause/continue breakpoint control is supported, but "step over" and "step into" aren't fully supported yet.
|
53
|
-
* inspecting primtive objects like strings and numbers works okay; support for more complex objects is in development.
|
54
56
|
* So far, ladybug has only been tested with simple Rails applications running on
|
55
57
|
Rails 5 with the puma web server. Eventually it aims to support more Rack
|
56
58
|
applications and web servers (and perhaps even non-Rack applications).
|
59
|
+
* inspecting primtive objects like strings and numbers works okay; support for more complex objects is in development.
|
57
60
|
|
58
61
|
## Author
|
59
62
|
|
data/lib/ladybug/debugger.rb
CHANGED
@@ -25,6 +25,8 @@ module Ladybug
|
|
25
25
|
|
26
26
|
@parsed_files = {}
|
27
27
|
|
28
|
+
@break = nil
|
29
|
+
|
28
30
|
# Todo: consider thread safety of mutating this hash
|
29
31
|
Thread.new do
|
30
32
|
preload_paths.each do |preload_path|
|
@@ -41,6 +43,20 @@ module Ladybug
|
|
41
43
|
set_trace_func trace_func
|
42
44
|
end
|
43
45
|
|
46
|
+
def debug
|
47
|
+
RubyVM::InstructionSequence.compile_option = {
|
48
|
+
trace_instruction: true
|
49
|
+
}
|
50
|
+
Thread.current.set_trace_func trace_func
|
51
|
+
|
52
|
+
yield
|
53
|
+
ensure
|
54
|
+
RubyVM::InstructionSequence.compile_option = {
|
55
|
+
trace_instruction: false
|
56
|
+
}
|
57
|
+
Thread.current.set_trace_func nil
|
58
|
+
end
|
59
|
+
|
44
60
|
def on_pause(&block)
|
45
61
|
@on_pause = block
|
46
62
|
end
|
@@ -131,12 +147,12 @@ module Ladybug
|
|
131
147
|
# remove ladybug code from a callstack and prepare it for comparison
|
132
148
|
# this is a hack implemenetation for now, can be made better
|
133
149
|
def clean(callstack)
|
134
|
-
callstack.drop_while { |frame| frame.to_s.include? "ladybug" }
|
150
|
+
callstack.drop_while { |frame| frame.to_s.include? "ladybug/debugger.rb" }
|
135
151
|
end
|
136
152
|
|
137
153
|
# If we're in step over/in/out mode,
|
138
154
|
# detect if we should break even if there's not a breakpoint set here
|
139
|
-
def break_on_step?
|
155
|
+
def break_on_step?(filename:)
|
140
156
|
# This is an important early return;
|
141
157
|
# we don't want to do anything w/ the callstack unless
|
142
158
|
# we're looking for a breakpoint, because
|
@@ -144,22 +160,21 @@ module Ladybug
|
|
144
160
|
# which makes things really slow
|
145
161
|
return false if @break.nil?
|
146
162
|
|
163
|
+
return false if @break == 'step_over' &&
|
164
|
+
@breakpoint_filename != filename
|
165
|
+
|
147
166
|
bp_callstack = clean(@breakpoint_callstack)
|
148
167
|
current_callstack = clean(Thread.current.backtrace_locations)
|
149
168
|
|
150
169
|
if @break == 'step_over'
|
151
|
-
return
|
170
|
+
return current_callstack[1].to_s == bp_callstack[1].to_s
|
152
171
|
elsif @break == 'step_into'
|
153
|
-
return
|
172
|
+
return current_callstack[1].to_s == bp_callstack[0].to_s
|
154
173
|
elsif @break == 'step_out'
|
155
|
-
return
|
174
|
+
return current_callstack[0].to_s == bp_callstack[1].to_s
|
156
175
|
end
|
157
176
|
end
|
158
177
|
|
159
|
-
def stacks_equal?(stack1, stack2)
|
160
|
-
stack1.map(&:to_s) == stack2.map(&:to_s)
|
161
|
-
end
|
162
|
-
|
163
178
|
def trace_func
|
164
179
|
proc { |event, filename, line_number, id, binding, klass, *rest|
|
165
180
|
# This check is called a lot so perhaps worth making faster,
|
@@ -168,12 +183,15 @@ module Ladybug
|
|
168
183
|
bp[:filename] == filename && bp[:line_number] == line_number
|
169
184
|
end
|
170
185
|
|
171
|
-
if breakpoint_hit || break_on_step?
|
186
|
+
if breakpoint_hit || break_on_step?(filename: filename)
|
172
187
|
local_variables =
|
173
188
|
binding.local_variables.each_with_object({}) do |lvar, hash|
|
174
189
|
hash[lvar] = binding.local_variable_get(lvar)
|
175
190
|
end
|
176
191
|
|
192
|
+
# todo: may want to offer classes the ability to
|
193
|
+
# override this and define which instance variables to expose here?
|
194
|
+
|
177
195
|
instance_variables =
|
178
196
|
binding.eval("instance_variables").each_with_object({}) do |ivar, hash|
|
179
197
|
hash[ivar] = binding.eval("instance_variable_get(:#{ivar})")
|
@@ -215,10 +233,12 @@ module Ladybug
|
|
215
233
|
case message[:command]
|
216
234
|
when 'continue'
|
217
235
|
@break = nil
|
236
|
+
@breakpoint_filename = nil
|
218
237
|
break
|
219
238
|
when 'step_over'
|
220
239
|
@break = 'step_over'
|
221
240
|
@breakpoint_callstack = Thread.current.backtrace_locations
|
241
|
+
@breakpoint_filename = filename
|
222
242
|
break
|
223
243
|
when 'step_into'
|
224
244
|
@break = 'step_into'
|
data/lib/ladybug/middleware.rb
CHANGED
@@ -31,7 +31,9 @@ module Ladybug
|
|
31
31
|
# Return async Rack response
|
32
32
|
ws.rack_response
|
33
33
|
else
|
34
|
-
@
|
34
|
+
@debugger.debug do
|
35
|
+
@app.call(env)
|
36
|
+
end
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
@@ -224,42 +226,50 @@ module Ladybug
|
|
224
226
|
|
225
227
|
script = @script_repository.find(absolute_path: info[:filename])
|
226
228
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
229
|
+
# currently we don't support going into functions
|
230
|
+
# that aren't in the path of our current app.
|
231
|
+
if script.nil?
|
232
|
+
puts "Debugger was paused on file outside of app: #{info[:filename]}"
|
233
|
+
puts "ladybug currently only supports pausing in app files."
|
234
|
+
@debugger.resume
|
235
|
+
else
|
236
|
+
location = {
|
237
|
+
scriptId: script.id,
|
238
|
+
lineNumber: info[:line_number] - 1,
|
239
|
+
columnNumber: 0
|
240
|
+
}
|
232
241
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
242
|
+
msg_to_client = {
|
243
|
+
method: "Debugger.paused",
|
244
|
+
params: {
|
245
|
+
callFrames: [
|
246
|
+
{
|
247
|
+
location: location,
|
248
|
+
callFrameId: SecureRandom.uuid,
|
249
|
+
functionName: info[:label],
|
250
|
+
scopeChain: [
|
251
|
+
{
|
252
|
+
type: "local",
|
253
|
+
startLocation: location,
|
254
|
+
endLocation: location,
|
255
|
+
object: {
|
256
|
+
className: "Object",
|
257
|
+
description: "Object",
|
258
|
+
type: "object",
|
259
|
+
objectId: object_id
|
260
|
+
}
|
251
261
|
}
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
262
|
+
],
|
263
|
+
url: script.virtual_url
|
264
|
+
}
|
265
|
+
],
|
266
|
+
hitBreakpoints: info[:breakpoint_id] ? [info[:breakpoint_id]] : [],
|
267
|
+
reason: "other"
|
268
|
+
}
|
259
269
|
}
|
260
|
-
}
|
261
270
|
|
262
|
-
|
271
|
+
ws.send(msg_to_client.to_json)
|
272
|
+
end
|
263
273
|
end
|
264
274
|
|
265
275
|
@debugger.on_resume do
|
@@ -271,8 +281,6 @@ module Ladybug
|
|
271
281
|
ws.send(msg_to_client.to_json)
|
272
282
|
end
|
273
283
|
|
274
|
-
@debugger.start
|
275
|
-
|
276
284
|
ws
|
277
285
|
end
|
278
286
|
end
|
@@ -99,12 +99,10 @@ module Ladybug
|
|
99
99
|
elsif object.is_a? Hash
|
100
100
|
object.
|
101
101
|
map do |key, value|
|
102
|
-
|
102
|
+
{
|
103
103
|
name: key,
|
104
104
|
value: serialize(value)
|
105
105
|
}
|
106
|
-
|
107
|
-
kv
|
108
106
|
end.
|
109
107
|
reject { |property| property[:value].nil? }
|
110
108
|
elsif object.is_a? Array
|