byebug 1.8.2 → 2.0.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.
- 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
|