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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -1
  3. data/GUIDE.md +14 -22
  4. data/README.md +69 -6
  5. data/bin/byebug +3 -20
  6. data/ext/byebug/breakpoint.c +185 -101
  7. data/ext/byebug/byebug.c +393 -214
  8. data/ext/byebug/byebug.h +34 -15
  9. data/ext/byebug/context.c +327 -102
  10. data/ext/byebug/extconf.rb +1 -1
  11. data/ext/byebug/locker.c +54 -0
  12. data/ext/byebug/threads.c +113 -0
  13. data/lib/byebug.rb +19 -58
  14. data/lib/byebug/command.rb +18 -19
  15. data/lib/byebug/commands/breakpoints.rb +1 -4
  16. data/lib/byebug/commands/catchpoint.rb +1 -1
  17. data/lib/byebug/commands/condition.rb +1 -1
  18. data/lib/byebug/commands/control.rb +2 -3
  19. data/lib/byebug/commands/display.rb +2 -7
  20. data/lib/byebug/commands/edit.rb +1 -1
  21. data/lib/byebug/commands/enable.rb +12 -12
  22. data/lib/byebug/commands/eval.rb +4 -4
  23. data/lib/byebug/commands/finish.rb +1 -1
  24. data/lib/byebug/commands/frame.rb +12 -8
  25. data/lib/byebug/commands/info.rb +20 -52
  26. data/lib/byebug/commands/kill.rb +1 -5
  27. data/lib/byebug/commands/list.rb +2 -1
  28. data/lib/byebug/commands/quit.rb +1 -1
  29. data/lib/byebug/commands/repl.rb +2 -2
  30. data/lib/byebug/commands/save.rb +1 -1
  31. data/lib/byebug/commands/set.rb +84 -90
  32. data/lib/byebug/commands/show.rb +44 -53
  33. data/lib/byebug/commands/skip.rb +1 -1
  34. data/lib/byebug/commands/stepping.rb +5 -4
  35. data/lib/byebug/commands/threads.rb +202 -0
  36. data/lib/byebug/commands/trace.rb +1 -1
  37. data/lib/byebug/helper.rb +3 -3
  38. data/lib/byebug/interface.rb +2 -20
  39. data/lib/byebug/processor.rb +21 -100
  40. data/lib/byebug/remote.rb +3 -3
  41. data/lib/byebug/version.rb +1 -1
  42. data/old_doc/byebug.1 +0 -6
  43. data/old_doc/byebug.texi +29 -46
  44. data/test/breakpoints_test.rb +44 -65
  45. data/test/conditions_test.rb +0 -9
  46. data/test/continue_test.rb +2 -2
  47. data/test/display_test.rb +4 -23
  48. data/test/edit_test.rb +2 -16
  49. data/test/eval_test.rb +4 -13
  50. data/test/examples/thread.rb +32 -0
  51. data/test/finish_test.rb +1 -13
  52. data/test/frame_test.rb +5 -12
  53. data/test/help_test.rb +2 -12
  54. data/test/info_test.rb +8 -18
  55. data/test/kill_test.rb +1 -10
  56. data/test/list_test.rb +5 -14
  57. data/test/method_test.rb +1 -10
  58. data/test/post_mortem_test.rb +247 -14
  59. data/test/quit_test.rb +0 -9
  60. data/test/reload_test.rb +1 -15
  61. data/test/repl_test.rb +1 -9
  62. data/test/restart_test.rb +3 -18
  63. data/test/save_test.rb +1 -13
  64. data/test/set_test.rb +35 -32
  65. data/test/show_test.rb +8 -27
  66. data/test/source_test.rb +1 -8
  67. data/test/stepping_test.rb +65 -96
  68. data/test/support/test_dsl.rb +12 -17
  69. data/test/test_helper.rb +1 -1
  70. data/test/thread_test.rb +106 -0
  71. data/test/trace_test.rb +5 -17
  72. data/test/variables_test.rb +1 -10
  73. metadata +9 -7
  74. data/lib/byebug/commands/jump.rb +0 -52
  75. data/test/jump_test.rb +0 -77
  76. data/test/support/context.rb +0 -15
@@ -9,7 +9,7 @@ end
9
9
 
10
10
  if RbConfig::MAKEFILE_CONFIG['CC'] =~ /gcc/
11
11
  $CFLAGS = '-Wall -Werror'
12
- $CFLAGS += ' -g3' if ENV['debug']
12
+ $CFLAGS += ' -gdwarf-2 -g3' if ENV['debug']
13
13
  end
14
14
 
15
15
  dir_config("ruby")
@@ -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
+ }
@@ -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
- alias stop remove_tracepoints
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
- if Byebug.started?
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.__debug_context ||
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.__debug_context, exp.__debug_file, exp.__debug_line)
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 :__debug_file, :__debug_line, :__debug_binding, :__debug_context
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.context.stop_return steps_out if steps_out >= 1
252
- Byebug.context.step_into steps_into if steps_into >= 0
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
@@ -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
- SubcmdStruct = Struct.new(:name, :min, :short_help, :long_help) unless
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(Byebug.const_get(:BYEBUG_DIR), 'commands', '*')].each {
92
- |file| require file if file =~ /\.rb$/ }
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(:force_stepping, false)
174
- register_setting_var(:frame_fullpath, true)
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(:tracing_plus, false)
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
- # :stack_trace_on_error - shows full stack trace if eval command results in
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
- # :force_stepping - stepping command always move to the new line
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 *del(?:ete)? (?:\s+(.*))?$/ix
103
+ /^\s* del(?:ete)? (?:\s+(.*))?$/x
107
104
  end
108
105
 
109
106
  def execute
@@ -4,7 +4,7 @@ module Byebug
4
4
  self.allow_in_control = true
5
5
 
6
6
  def regexp
7
- /^\s* cat(?:ch)? (?:\s+(\S+))? (?:\s+(off))? \s*$/ix
7
+ /^\s* cat(?:ch)? (?:\s+(\S+))? (?:\s+(off))? \s*$/x
8
8
  end
9
9
 
10
10
  def execute
@@ -3,7 +3,7 @@ module Byebug
3
3
  class ConditionCommand < Command
4
4
 
5
5
  def regexp
6
- /^\s* cond(?:ition)? (?:\s+(\d+)(?:\s+(.*))?)? \s*$/ix
6
+ /^\s* cond(?:ition)? (?:\s+(\d+)(?:\s+(.*))?)? \s*$/x
7
7
  end
8
8
 
9
9
  def execute