byebug 1.8.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -1
- data/GUIDE.md +14 -22
- data/README.md +69 -6
- data/bin/byebug +3 -20
- data/ext/byebug/breakpoint.c +185 -101
- data/ext/byebug/byebug.c +393 -214
- data/ext/byebug/byebug.h +34 -15
- data/ext/byebug/context.c +327 -102
- data/ext/byebug/extconf.rb +1 -1
- data/ext/byebug/locker.c +54 -0
- data/ext/byebug/threads.c +113 -0
- data/lib/byebug.rb +19 -58
- data/lib/byebug/command.rb +18 -19
- data/lib/byebug/commands/breakpoints.rb +1 -4
- data/lib/byebug/commands/catchpoint.rb +1 -1
- data/lib/byebug/commands/condition.rb +1 -1
- data/lib/byebug/commands/control.rb +2 -3
- data/lib/byebug/commands/display.rb +2 -7
- data/lib/byebug/commands/edit.rb +1 -1
- data/lib/byebug/commands/enable.rb +12 -12
- data/lib/byebug/commands/eval.rb +4 -4
- data/lib/byebug/commands/finish.rb +1 -1
- data/lib/byebug/commands/frame.rb +12 -8
- data/lib/byebug/commands/info.rb +20 -52
- data/lib/byebug/commands/kill.rb +1 -5
- data/lib/byebug/commands/list.rb +2 -1
- data/lib/byebug/commands/quit.rb +1 -1
- data/lib/byebug/commands/repl.rb +2 -2
- data/lib/byebug/commands/save.rb +1 -1
- data/lib/byebug/commands/set.rb +84 -90
- data/lib/byebug/commands/show.rb +44 -53
- data/lib/byebug/commands/skip.rb +1 -1
- data/lib/byebug/commands/stepping.rb +5 -4
- data/lib/byebug/commands/threads.rb +202 -0
- data/lib/byebug/commands/trace.rb +1 -1
- data/lib/byebug/helper.rb +3 -3
- data/lib/byebug/interface.rb +2 -20
- data/lib/byebug/processor.rb +21 -100
- data/lib/byebug/remote.rb +3 -3
- data/lib/byebug/version.rb +1 -1
- data/old_doc/byebug.1 +0 -6
- data/old_doc/byebug.texi +29 -46
- data/test/breakpoints_test.rb +44 -65
- data/test/conditions_test.rb +0 -9
- data/test/continue_test.rb +2 -2
- data/test/display_test.rb +4 -23
- data/test/edit_test.rb +2 -16
- data/test/eval_test.rb +4 -13
- data/test/examples/thread.rb +32 -0
- data/test/finish_test.rb +1 -13
- data/test/frame_test.rb +5 -12
- data/test/help_test.rb +2 -12
- data/test/info_test.rb +8 -18
- data/test/kill_test.rb +1 -10
- data/test/list_test.rb +5 -14
- data/test/method_test.rb +1 -10
- data/test/post_mortem_test.rb +247 -14
- data/test/quit_test.rb +0 -9
- data/test/reload_test.rb +1 -15
- data/test/repl_test.rb +1 -9
- data/test/restart_test.rb +3 -18
- data/test/save_test.rb +1 -13
- data/test/set_test.rb +35 -32
- data/test/show_test.rb +8 -27
- data/test/source_test.rb +1 -8
- data/test/stepping_test.rb +65 -96
- data/test/support/test_dsl.rb +12 -17
- data/test/test_helper.rb +1 -1
- data/test/thread_test.rb +106 -0
- data/test/trace_test.rb +5 -17
- data/test/variables_test.rb +1 -10
- metadata +9 -7
- data/lib/byebug/commands/jump.rb +0 -52
- data/test/jump_test.rb +0 -77
- data/test/support/context.rb +0 -15
data/ext/byebug/extconf.rb
CHANGED
data/ext/byebug/locker.c
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#include <byebug.h>
|
2
|
+
|
3
|
+
typedef struct locked_thread_t {
|
4
|
+
VALUE thread;
|
5
|
+
struct locked_thread_t *next;
|
6
|
+
} locked_thread_t;
|
7
|
+
|
8
|
+
static locked_thread_t *locked_head = NULL;
|
9
|
+
static locked_thread_t *locked_tail = NULL;
|
10
|
+
|
11
|
+
extern int
|
12
|
+
is_in_locked(VALUE thread)
|
13
|
+
{
|
14
|
+
locked_thread_t *node;
|
15
|
+
|
16
|
+
if (!locked_head) return 0;
|
17
|
+
|
18
|
+
for (node = locked_head; node != locked_tail; node = node->next)
|
19
|
+
{
|
20
|
+
if (node->thread == thread) return 1;
|
21
|
+
}
|
22
|
+
return 0;
|
23
|
+
}
|
24
|
+
|
25
|
+
extern void
|
26
|
+
add_to_locked(VALUE thread)
|
27
|
+
{
|
28
|
+
locked_thread_t *node;
|
29
|
+
|
30
|
+
if (is_in_locked(thread)) return;
|
31
|
+
|
32
|
+
node = ALLOC(locked_thread_t);
|
33
|
+
node->thread = thread;
|
34
|
+
node->next = NULL;
|
35
|
+
if (locked_tail) locked_tail->next = node;
|
36
|
+
locked_tail = node;
|
37
|
+
if (!locked_head) locked_head = node;
|
38
|
+
}
|
39
|
+
|
40
|
+
extern VALUE
|
41
|
+
remove_from_locked()
|
42
|
+
{
|
43
|
+
VALUE thread;
|
44
|
+
locked_thread_t *node;
|
45
|
+
|
46
|
+
if (locked_head == NULL) return Qnil;
|
47
|
+
|
48
|
+
node = locked_head;
|
49
|
+
locked_head = locked_head->next;
|
50
|
+
if (locked_tail == node) locked_tail = NULL;
|
51
|
+
thread = node->thread;
|
52
|
+
xfree(node);
|
53
|
+
return thread;
|
54
|
+
}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
#include <byebug.h>
|
2
|
+
|
3
|
+
static int
|
4
|
+
t_tbl_mark_keyvalue(st_data_t key, st_data_t value, st_data_t tbl)
|
5
|
+
{
|
6
|
+
VALUE thread = (VALUE)key;
|
7
|
+
|
8
|
+
if (!value) return ST_CONTINUE;
|
9
|
+
|
10
|
+
rb_gc_mark((VALUE)value);
|
11
|
+
rb_gc_mark(thread);
|
12
|
+
|
13
|
+
return ST_CONTINUE;
|
14
|
+
}
|
15
|
+
|
16
|
+
static void
|
17
|
+
t_tbl_mark(void* data)
|
18
|
+
{
|
19
|
+
threads_table_t *t_tbl = (threads_table_t *)data;
|
20
|
+
st_table *tbl = t_tbl->tbl;
|
21
|
+
st_foreach(tbl, t_tbl_mark_keyvalue, (st_data_t)tbl);
|
22
|
+
}
|
23
|
+
|
24
|
+
static void
|
25
|
+
t_tbl_free(void* data)
|
26
|
+
{
|
27
|
+
threads_table_t *t_tbl = (threads_table_t*)data;
|
28
|
+
st_free_table(t_tbl->tbl);
|
29
|
+
xfree(t_tbl);
|
30
|
+
}
|
31
|
+
|
32
|
+
VALUE
|
33
|
+
threads_create(void)
|
34
|
+
{
|
35
|
+
threads_table_t *t_tbl;
|
36
|
+
|
37
|
+
t_tbl = ALLOC(threads_table_t);
|
38
|
+
t_tbl->tbl = st_init_numtable();
|
39
|
+
return Data_Wrap_Struct(cThreadsTable, t_tbl_mark, t_tbl_free, t_tbl);
|
40
|
+
}
|
41
|
+
|
42
|
+
void
|
43
|
+
threads_clear(VALUE table)
|
44
|
+
{
|
45
|
+
threads_table_t *t_tbl;
|
46
|
+
|
47
|
+
Data_Get_Struct(table, threads_table_t, t_tbl);
|
48
|
+
st_clear(t_tbl->tbl);
|
49
|
+
}
|
50
|
+
|
51
|
+
static int
|
52
|
+
is_living_thread(VALUE thread)
|
53
|
+
{
|
54
|
+
return rb_funcall(thread, rb_intern("alive?"), 0) == Qtrue;
|
55
|
+
}
|
56
|
+
|
57
|
+
static int
|
58
|
+
t_tbl_check_i(st_data_t key, st_data_t value, st_data_t dummy)
|
59
|
+
{
|
60
|
+
VALUE thread;
|
61
|
+
|
62
|
+
if (!value) return ST_DELETE;
|
63
|
+
|
64
|
+
thread = (VALUE)key;
|
65
|
+
|
66
|
+
if (!is_living_thread(thread)) return ST_DELETE;
|
67
|
+
|
68
|
+
return ST_CONTINUE;
|
69
|
+
}
|
70
|
+
|
71
|
+
void
|
72
|
+
check_thread_contexts(void)
|
73
|
+
{
|
74
|
+
threads_table_t *t_tbl;
|
75
|
+
|
76
|
+
Data_Get_Struct(threads, threads_table_t, t_tbl);
|
77
|
+
st_foreach(t_tbl->tbl, t_tbl_check_i, 0);
|
78
|
+
}
|
79
|
+
|
80
|
+
void
|
81
|
+
thread_context_lookup(VALUE thread, VALUE *context)
|
82
|
+
{
|
83
|
+
threads_table_t *t_tbl;
|
84
|
+
|
85
|
+
Data_Get_Struct(threads, threads_table_t, t_tbl);
|
86
|
+
if (!st_lookup(t_tbl->tbl, thread, context) || !*context)
|
87
|
+
{
|
88
|
+
*context = context_create(thread);
|
89
|
+
st_insert(t_tbl->tbl, thread, *context);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
void
|
94
|
+
halt_while_other_thread_is_active(debug_context_t *dc)
|
95
|
+
{
|
96
|
+
while (1)
|
97
|
+
{
|
98
|
+
/* halt execution of current thread if debugger is activated in another */
|
99
|
+
while (locker != Qnil && locker != rb_thread_current())
|
100
|
+
{
|
101
|
+
add_to_locked(rb_thread_current());
|
102
|
+
rb_thread_stop();
|
103
|
+
}
|
104
|
+
|
105
|
+
/* stop the current thread if it's marked as suspended */
|
106
|
+
if (CTX_FL_TEST(dc, CTX_FL_SUSPEND) && locker != rb_thread_current())
|
107
|
+
{
|
108
|
+
CTX_FL_SET(dc, CTX_FL_WAS_RUNNING);
|
109
|
+
rb_thread_stop();
|
110
|
+
}
|
111
|
+
else break;
|
112
|
+
}
|
113
|
+
}
|
data/lib/byebug.rb
CHANGED
@@ -29,9 +29,6 @@ module Byebug
|
|
29
29
|
attr_accessor :last_exception
|
30
30
|
Byebug.last_exception = nil
|
31
31
|
|
32
|
-
# gdb-style annotation mode. Used in GNU Emacs interface
|
33
|
-
attr_accessor :annotate
|
34
|
-
|
35
32
|
def source_reload
|
36
33
|
Object.send(:remove_const, "SCRIPT_LINES__") if
|
37
34
|
Object.const_defined?("SCRIPT_LINES__")
|
@@ -39,8 +36,11 @@ module Byebug
|
|
39
36
|
LineCache::clear_file_cache
|
40
37
|
end
|
41
38
|
|
39
|
+
#
|
42
40
|
# Get line +line_number+ from file named +filename+.
|
41
|
+
#
|
43
42
|
# @return "\n" if there was a problem. Leaking blanks are stripped off.
|
43
|
+
#
|
44
44
|
def line_at(filename, line_number)
|
45
45
|
@@autoreload = nil unless defined?(@@autoreload)
|
46
46
|
line = LineCache::getline filename, line_number, @@autoreload
|
@@ -48,11 +48,13 @@ module Byebug
|
|
48
48
|
return "#{line.gsub(/^\s+/, '').chomp}"
|
49
49
|
end
|
50
50
|
|
51
|
-
|
52
|
-
|
51
|
+
#
|
52
|
+
# Add a new breakpoint
|
53
|
+
#
|
53
54
|
# @param [String] file
|
54
55
|
# @param [Fixnum] line
|
55
56
|
# @param [String] expr
|
57
|
+
#
|
56
58
|
def add_breakpoint(file, line, expr=nil)
|
57
59
|
breakpoint = Breakpoint.new(file, line, expr)
|
58
60
|
breakpoints << breakpoint
|
@@ -104,16 +106,12 @@ module Byebug
|
|
104
106
|
Byebug.const_set('INITIAL_DIR', Dir.pwd) unless defined? Byebug::INITIAL_DIR
|
105
107
|
end
|
106
108
|
Byebug.tracing = options[:tracing] unless options[:tracing].nil?
|
107
|
-
|
108
|
-
retval = block && block.call(self)
|
109
|
-
else
|
110
|
-
retval = Byebug._start(&block)
|
111
|
-
end
|
109
|
+
retval = Byebug._start(&block)
|
112
110
|
post_mortem if options[:post_mortem]
|
113
111
|
return retval
|
114
112
|
end
|
115
113
|
|
116
|
-
|
114
|
+
#
|
117
115
|
# Runs normal byebug initialization scripts.
|
118
116
|
#
|
119
117
|
# Reads and executes the commands from init file (if any) in the current
|
@@ -132,7 +130,7 @@ module Byebug
|
|
132
130
|
end
|
133
131
|
end
|
134
132
|
|
135
|
-
|
133
|
+
#
|
136
134
|
# Runs a script file
|
137
135
|
#
|
138
136
|
def run_script(file, out = handler.interface, verbose=false)
|
@@ -141,7 +139,7 @@ module Byebug
|
|
141
139
|
processor.process_commands(verbose)
|
142
140
|
end
|
143
141
|
|
144
|
-
|
142
|
+
#
|
145
143
|
# Activates the post-mortem mode. There are two ways of using it:
|
146
144
|
#
|
147
145
|
# == Global post-mortem mode
|
@@ -185,70 +183,33 @@ module Byebug
|
|
185
183
|
end
|
186
184
|
|
187
185
|
def handle_post_mortem(exp)
|
188
|
-
return if !exp || !exp.
|
189
|
-
exp.__debug_context.stack_size == 0
|
186
|
+
return if !exp || !exp.__bb_context || exp.__bb_context.stack_size == 0
|
190
187
|
orig_tracing = Byebug.tracing?
|
191
188
|
Byebug.tracing = false
|
192
189
|
Byebug.last_exception = exp
|
193
|
-
handler.at_line(exp.
|
190
|
+
handler.at_line(exp.__bb_context, exp.__bb_file, exp.__bb_line)
|
194
191
|
ensure
|
195
192
|
Byebug.tracing = orig_tracing
|
196
193
|
end
|
197
194
|
private :handle_post_mortem
|
198
|
-
|
199
195
|
end
|
200
196
|
end
|
201
197
|
|
202
198
|
class Exception
|
203
|
-
attr_reader :
|
204
|
-
end
|
205
|
-
|
206
|
-
class Module
|
207
|
-
#
|
208
|
-
# Wraps the +meth+ method with Byebug.start {...} block.
|
209
|
-
#
|
210
|
-
def debug_method(meth)
|
211
|
-
old_meth = "__debugee_#{meth}"
|
212
|
-
old_meth = "#{$1}_set" if old_meth =~ /^(.+)=$/
|
213
|
-
alias_method old_meth.to_sym, meth
|
214
|
-
class_eval <<-EOD
|
215
|
-
def #{meth}(*args, &block)
|
216
|
-
Byebug.start do
|
217
|
-
byebug 2
|
218
|
-
#{old_meth}(*args, &block)
|
219
|
-
end
|
220
|
-
end
|
221
|
-
EOD
|
222
|
-
end
|
223
|
-
|
224
|
-
#
|
225
|
-
# Wraps the +meth+ method with Byebug.post_mortem {...} block.
|
226
|
-
#
|
227
|
-
def post_mortem_method(meth)
|
228
|
-
old_meth = "__postmortem_#{meth}"
|
229
|
-
old_meth = "#{$1}_set" if old_meth =~ /^(.+)=$/
|
230
|
-
alias_method old_meth.to_sym, meth
|
231
|
-
class_eval <<-EOD
|
232
|
-
def #{meth}(*args, &block)
|
233
|
-
Byebug.start do |dbg|
|
234
|
-
dbg.post_mortem do
|
235
|
-
#{old_meth}(*args, &block)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
EOD
|
240
|
-
end
|
199
|
+
attr_reader :__bb_file, :__bb_line, :__bb_binding, :__bb_context
|
241
200
|
end
|
242
201
|
|
243
202
|
module Kernel
|
244
|
-
|
203
|
+
#
|
245
204
|
# Enters byebug after _steps_into_ line events and _steps_out_ return events
|
246
205
|
# occur. Before entering byebug startup, the init script is read.
|
247
206
|
#
|
248
207
|
def byebug(steps_into = 1, steps_out = 2)
|
249
208
|
Byebug.start
|
250
209
|
Byebug.run_init_script(StringIO.new)
|
251
|
-
Byebug.
|
252
|
-
|
210
|
+
if Byebug.current_context.stack_size > 2
|
211
|
+
Byebug.current_context.stop_return steps_out if steps_out >= 1
|
212
|
+
end
|
213
|
+
Byebug.current_context.step_into steps_into if steps_into >= 0
|
253
214
|
end
|
254
215
|
end
|
data/lib/byebug/command.rb
CHANGED
@@ -5,7 +5,7 @@ require_relative 'helper'
|
|
5
5
|
module Byebug
|
6
6
|
|
7
7
|
module CommandFunctions
|
8
|
-
|
8
|
+
#
|
9
9
|
# Pad a string with dots at the end to fit :width setting
|
10
10
|
#
|
11
11
|
def pad_with_dots(string)
|
@@ -15,13 +15,8 @@ module Byebug
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
# Root dir for byebug
|
19
|
-
BYEBUG_DIR = File.expand_path(File.dirname(__FILE__)) unless
|
20
|
-
defined?(BYEBUG_DIR)
|
21
|
-
|
22
18
|
class Command
|
23
|
-
|
24
|
-
defined?(SubcmdStruct)
|
19
|
+
Subcmd = Struct.new(:name, :min, :short_help, :long_help)
|
25
20
|
|
26
21
|
class << self
|
27
22
|
def commands
|
@@ -88,8 +83,8 @@ module Byebug
|
|
88
83
|
end
|
89
84
|
|
90
85
|
def load_commands
|
91
|
-
Dir[File.join(
|
92
|
-
|file| require file
|
86
|
+
Dir[File.join(File.dirname(__FILE__), 'commands', '*')].each {
|
87
|
+
|file| require file }
|
93
88
|
Byebug.constants.grep(/Functions$/).map {
|
94
89
|
|name| Byebug.const_get(name) }.each { |mod| include mod }
|
95
90
|
end
|
@@ -170,11 +165,11 @@ module Byebug
|
|
170
165
|
register_setting_var(:basename, false)
|
171
166
|
register_setting_var(:callstyle, :long)
|
172
167
|
register_setting_var(:testing, false)
|
173
|
-
register_setting_var(:
|
174
|
-
register_setting_var(:
|
168
|
+
register_setting_var(:forcestep, false)
|
169
|
+
register_setting_var(:fullpath, true)
|
175
170
|
register_setting_var(:listsize, 10)
|
176
171
|
register_setting_var(:stack_trace_on_error, false)
|
177
|
-
register_setting_var(:
|
172
|
+
register_setting_var(:linetrace_plus, false)
|
178
173
|
cols = terminal_width || 160
|
179
174
|
register_setting_var(:width, cols > 10 ? cols : 160)
|
180
175
|
Byebug::ARGV = ARGV.clone unless defined? Byebug::ARGV
|
@@ -232,6 +227,10 @@ module Byebug
|
|
232
227
|
def get_binding pos = @state.frame_pos
|
233
228
|
@state.context ? @state.context.frame_binding(pos) : TOPLEVEL_BINDING
|
234
229
|
end
|
230
|
+
|
231
|
+
def get_context(thnum)
|
232
|
+
Byebug.contexts.find {|c| c.thnum == thnum}
|
233
|
+
end
|
235
234
|
end
|
236
235
|
|
237
236
|
Command.load_commands
|
@@ -241,18 +240,18 @@ module Byebug
|
|
241
240
|
# Use Byebug.settings[] and Byebug.settings[]= methods to query and set
|
242
241
|
# byebug settings. These settings are available:
|
243
242
|
#
|
244
|
-
# :autolist - automatically calls 'list' command on breakpoint
|
245
243
|
# :autoeval - evaluates input in the current binding if it's not
|
246
244
|
# recognized as a byebug command
|
247
245
|
# :autoirb - automatically calls 'irb' command on breakpoint
|
248
|
-
# :
|
249
|
-
# an exception
|
250
|
-
# :frame_fullpath - displays full paths when showing frame stack
|
251
|
-
# :frame_class_names - displays method's class name when showing frame
|
252
|
-
# stack
|
246
|
+
# :autolist - automatically calls 'list' command on breakpoint
|
253
247
|
# :autoreload - makes 'list' command always display up-to-date
|
254
248
|
# source code
|
255
|
-
# :
|
249
|
+
# :frame_class_names - displays method's class name when showing frame
|
250
|
+
# stack
|
251
|
+
# :forcestep - stepping command always move to the new line
|
252
|
+
# :fullpath - displays full paths when showing frame stack
|
253
|
+
# :stack_trace_on_error - shows full stack trace if eval command results in
|
254
|
+
# an exception
|
256
255
|
#
|
257
256
|
def self.settings
|
258
257
|
Command.settings
|
@@ -66,9 +66,6 @@ module Byebug
|
|
66
66
|
return unless confirm("Set breakpoint anyway? (y/n) ")
|
67
67
|
end
|
68
68
|
|
69
|
-
return errmsg "We are not in a state we can add breakpoints.\n" unless
|
70
|
-
@state.context
|
71
|
-
|
72
69
|
b = Byebug.add_breakpoint brkpt_filename, line, expr
|
73
70
|
print "Created breakpoint #{b.id} at " \
|
74
71
|
"#{CommandProcessor.canonic_file(brkpt_filename)}:#{line.to_s}\n"
|
@@ -103,7 +100,7 @@ module Byebug
|
|
103
100
|
self.allow_in_control = true
|
104
101
|
|
105
102
|
def regexp
|
106
|
-
/^\s
|
103
|
+
/^\s* del(?:ete)? (?:\s+(.*))?$/x
|
107
104
|
end
|
108
105
|
|
109
106
|
def execute
|