byebug 11.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +897 -0
  3. data/CONTRIBUTING.md +58 -0
  4. data/GUIDE.md +1806 -0
  5. data/LICENSE +23 -0
  6. data/README.md +199 -0
  7. data/exe/byebug +6 -0
  8. data/ext/byebug/breakpoint.c +517 -0
  9. data/ext/byebug/byebug.c +905 -0
  10. data/ext/byebug/byebug.h +143 -0
  11. data/ext/byebug/context.c +673 -0
  12. data/ext/byebug/extconf.rb +12 -0
  13. data/ext/byebug/locker.c +96 -0
  14. data/ext/byebug/threads.c +230 -0
  15. data/lib/byebug.rb +3 -0
  16. data/lib/byebug/attacher.rb +48 -0
  17. data/lib/byebug/breakpoint.rb +111 -0
  18. data/lib/byebug/command.rb +111 -0
  19. data/lib/byebug/command_list.rb +34 -0
  20. data/lib/byebug/commands.rb +40 -0
  21. data/lib/byebug/commands/break.rb +112 -0
  22. data/lib/byebug/commands/catch.rb +78 -0
  23. data/lib/byebug/commands/condition.rb +55 -0
  24. data/lib/byebug/commands/continue.rb +68 -0
  25. data/lib/byebug/commands/debug.rb +38 -0
  26. data/lib/byebug/commands/delete.rb +55 -0
  27. data/lib/byebug/commands/disable.rb +33 -0
  28. data/lib/byebug/commands/disable/breakpoints.rb +42 -0
  29. data/lib/byebug/commands/disable/display.rb +43 -0
  30. data/lib/byebug/commands/display.rb +66 -0
  31. data/lib/byebug/commands/down.rb +45 -0
  32. data/lib/byebug/commands/edit.rb +69 -0
  33. data/lib/byebug/commands/enable.rb +33 -0
  34. data/lib/byebug/commands/enable/breakpoints.rb +42 -0
  35. data/lib/byebug/commands/enable/display.rb +43 -0
  36. data/lib/byebug/commands/finish.rb +57 -0
  37. data/lib/byebug/commands/frame.rb +57 -0
  38. data/lib/byebug/commands/help.rb +64 -0
  39. data/lib/byebug/commands/history.rb +39 -0
  40. data/lib/byebug/commands/info.rb +37 -0
  41. data/lib/byebug/commands/info/breakpoints.rb +65 -0
  42. data/lib/byebug/commands/info/display.rb +49 -0
  43. data/lib/byebug/commands/info/file.rb +80 -0
  44. data/lib/byebug/commands/info/line.rb +35 -0
  45. data/lib/byebug/commands/info/program.rb +49 -0
  46. data/lib/byebug/commands/interrupt.rb +34 -0
  47. data/lib/byebug/commands/irb.rb +50 -0
  48. data/lib/byebug/commands/kill.rb +45 -0
  49. data/lib/byebug/commands/list.rb +159 -0
  50. data/lib/byebug/commands/method.rb +53 -0
  51. data/lib/byebug/commands/next.rb +40 -0
  52. data/lib/byebug/commands/pry.rb +41 -0
  53. data/lib/byebug/commands/quit.rb +42 -0
  54. data/lib/byebug/commands/restart.rb +64 -0
  55. data/lib/byebug/commands/save.rb +72 -0
  56. data/lib/byebug/commands/set.rb +79 -0
  57. data/lib/byebug/commands/show.rb +45 -0
  58. data/lib/byebug/commands/skip.rb +85 -0
  59. data/lib/byebug/commands/source.rb +40 -0
  60. data/lib/byebug/commands/step.rb +40 -0
  61. data/lib/byebug/commands/thread.rb +34 -0
  62. data/lib/byebug/commands/thread/current.rb +37 -0
  63. data/lib/byebug/commands/thread/list.rb +43 -0
  64. data/lib/byebug/commands/thread/resume.rb +45 -0
  65. data/lib/byebug/commands/thread/stop.rb +43 -0
  66. data/lib/byebug/commands/thread/switch.rb +46 -0
  67. data/lib/byebug/commands/tracevar.rb +54 -0
  68. data/lib/byebug/commands/undisplay.rb +51 -0
  69. data/lib/byebug/commands/untracevar.rb +36 -0
  70. data/lib/byebug/commands/up.rb +45 -0
  71. data/lib/byebug/commands/var.rb +37 -0
  72. data/lib/byebug/commands/var/all.rb +41 -0
  73. data/lib/byebug/commands/var/args.rb +39 -0
  74. data/lib/byebug/commands/var/const.rb +49 -0
  75. data/lib/byebug/commands/var/global.rb +37 -0
  76. data/lib/byebug/commands/var/instance.rb +39 -0
  77. data/lib/byebug/commands/var/local.rb +39 -0
  78. data/lib/byebug/commands/where.rb +53 -0
  79. data/lib/byebug/context.rb +157 -0
  80. data/lib/byebug/core.rb +115 -0
  81. data/lib/byebug/errors.rb +29 -0
  82. data/lib/byebug/frame.rb +185 -0
  83. data/lib/byebug/helpers/bin.rb +47 -0
  84. data/lib/byebug/helpers/eval.rb +126 -0
  85. data/lib/byebug/helpers/file.rb +63 -0
  86. data/lib/byebug/helpers/frame.rb +75 -0
  87. data/lib/byebug/helpers/parse.rb +75 -0
  88. data/lib/byebug/helpers/path.rb +40 -0
  89. data/lib/byebug/helpers/reflection.rb +19 -0
  90. data/lib/byebug/helpers/string.rb +33 -0
  91. data/lib/byebug/helpers/thread.rb +67 -0
  92. data/lib/byebug/helpers/toggle.rb +62 -0
  93. data/lib/byebug/helpers/var.rb +54 -0
  94. data/lib/byebug/history.rb +130 -0
  95. data/lib/byebug/interface.rb +146 -0
  96. data/lib/byebug/interfaces/local_interface.rb +44 -0
  97. data/lib/byebug/interfaces/remote_interface.rb +50 -0
  98. data/lib/byebug/interfaces/script_interface.rb +33 -0
  99. data/lib/byebug/interfaces/test_interface.rb +67 -0
  100. data/lib/byebug/option_setter.rb +95 -0
  101. data/lib/byebug/printers/base.rb +68 -0
  102. data/lib/byebug/printers/plain.rb +44 -0
  103. data/lib/byebug/printers/texts/base.yml +115 -0
  104. data/lib/byebug/printers/texts/plain.yml +33 -0
  105. data/lib/byebug/processors/command_processor.rb +173 -0
  106. data/lib/byebug/processors/control_processor.rb +24 -0
  107. data/lib/byebug/processors/post_mortem_processor.rb +18 -0
  108. data/lib/byebug/processors/script_processor.rb +49 -0
  109. data/lib/byebug/remote.rb +85 -0
  110. data/lib/byebug/remote/client.rb +57 -0
  111. data/lib/byebug/remote/server.rb +47 -0
  112. data/lib/byebug/runner.rb +198 -0
  113. data/lib/byebug/setting.rb +79 -0
  114. data/lib/byebug/settings/autoirb.rb +29 -0
  115. data/lib/byebug/settings/autolist.rb +29 -0
  116. data/lib/byebug/settings/autopry.rb +29 -0
  117. data/lib/byebug/settings/autosave.rb +17 -0
  118. data/lib/byebug/settings/basename.rb +16 -0
  119. data/lib/byebug/settings/callstyle.rb +20 -0
  120. data/lib/byebug/settings/fullpath.rb +16 -0
  121. data/lib/byebug/settings/histfile.rb +20 -0
  122. data/lib/byebug/settings/histsize.rb +20 -0
  123. data/lib/byebug/settings/linetrace.rb +22 -0
  124. data/lib/byebug/settings/listsize.rb +21 -0
  125. data/lib/byebug/settings/post_mortem.rb +27 -0
  126. data/lib/byebug/settings/savefile.rb +20 -0
  127. data/lib/byebug/settings/stack_on_error.rb +15 -0
  128. data/lib/byebug/settings/width.rb +20 -0
  129. data/lib/byebug/source_file_formatter.rb +71 -0
  130. data/lib/byebug/subcommands.rb +54 -0
  131. data/lib/byebug/version.rb +8 -0
  132. metadata +199 -0
@@ -0,0 +1,905 @@
1
+ #include "byebug.h"
2
+
3
+ static VALUE mByebug; /* Ruby Byebug Module object */
4
+
5
+ static VALUE tracing = Qfalse;
6
+ static VALUE post_mortem = Qfalse;
7
+ static VALUE verbose = Qfalse;
8
+
9
+ static VALUE catchpoints = Qnil;
10
+ static VALUE breakpoints = Qnil;
11
+ static VALUE tracepoints = Qnil;
12
+
13
+ static VALUE raised_exception = Qnil;
14
+
15
+ static ID idPuts;
16
+ static ID idEmpty;
17
+
18
+ /* Hash table with active threads and their associated contexts */
19
+ VALUE threads = Qnil;
20
+
21
+ /*
22
+ * call-seq:
23
+ * Byebug.breakpoints -> array
24
+ *
25
+ * Returns an array of breakpoints.
26
+ */
27
+ static VALUE
28
+ Breakpoints(VALUE self)
29
+ {
30
+ UNUSED(self);
31
+
32
+ if (NIL_P(breakpoints))
33
+ breakpoints = rb_ary_new();
34
+
35
+ return breakpoints;
36
+ }
37
+
38
+ /*
39
+ * call-seq:
40
+ * Byebug.catchpoints -> hash
41
+ *
42
+ * Returns the catchpoints hash.
43
+ */
44
+ static VALUE
45
+ Catchpoints(VALUE self)
46
+ {
47
+ UNUSED(self);
48
+
49
+ return catchpoints;
50
+ }
51
+
52
+ /*
53
+ * call-seq:
54
+ * Byebug.raised_exception -> exception
55
+ *
56
+ * Returns raised exception when in post_mortem mode.
57
+ */
58
+ static VALUE
59
+ Raised_exception(VALUE self)
60
+ {
61
+ UNUSED(self);
62
+
63
+ return raised_exception;
64
+ }
65
+
66
+ #define IS_STARTED (!NIL_P(catchpoints))
67
+
68
+ static void
69
+ check_started()
70
+ {
71
+ if (!IS_STARTED)
72
+ {
73
+ rb_raise(rb_eRuntimeError, "Byebug is not started yet.");
74
+ }
75
+ }
76
+
77
+ static void
78
+ trace_print(rb_trace_arg_t *trace_arg, debug_context_t *dc,
79
+ const char *file_filter, const char *debug_msg)
80
+ {
81
+ char *fullpath = NULL;
82
+ const char *basename;
83
+ int filtered = 0;
84
+ const char *event = rb_id2name(SYM2ID(rb_tracearg_event(trace_arg)));
85
+
86
+ VALUE rb_path = rb_tracearg_path(trace_arg);
87
+ const char *path = NIL_P(rb_path) ? "" : RSTRING_PTR(rb_path);
88
+
89
+ int line = NUM2INT(rb_tracearg_lineno(trace_arg));
90
+
91
+ VALUE rb_mid = rb_tracearg_method_id(trace_arg);
92
+ const char *mid = NIL_P(rb_mid) ? "(top level)" : rb_id2name(SYM2ID(rb_mid));
93
+
94
+ VALUE rb_cl = rb_tracearg_defined_class(trace_arg);
95
+ VALUE rb_cl_name = NIL_P(rb_cl) ? rb_cl : rb_mod_name(rb_cl);
96
+ const char *defined_class = NIL_P(rb_cl_name) ? "" : RSTRING_PTR(rb_cl_name);
97
+
98
+ if (!trace_arg)
99
+ return;
100
+
101
+ if (file_filter)
102
+ {
103
+ #ifndef _WIN32
104
+ fullpath = realpath(path, NULL);
105
+ #endif
106
+ basename = fullpath ? strrchr(fullpath, '/') : path;
107
+
108
+ if (!basename || strncmp(basename + 1, file_filter, strlen(file_filter)))
109
+ filtered = 1;
110
+
111
+ #ifndef _WIN32
112
+ free(fullpath);
113
+ #endif
114
+ }
115
+
116
+ if (!filtered)
117
+ {
118
+ if (debug_msg)
119
+ rb_funcall(mByebug, idPuts, 1,
120
+ rb_sprintf("[#%d] %s\n", dc->thnum, debug_msg));
121
+ else
122
+ rb_funcall(mByebug, idPuts, 1,
123
+ rb_sprintf("%*s [#%d] %s@%s:%d %s#%s\n", dc->calced_stack_size,
124
+ "", dc->thnum, event, path, line, defined_class,
125
+ mid));
126
+ }
127
+ }
128
+
129
+ static void
130
+ cleanup(debug_context_t *dc)
131
+ {
132
+ dc->stop_reason = CTX_STOP_NONE;
133
+
134
+ release_lock();
135
+ }
136
+
137
+ #define EVENT_TEARDOWN cleanup(dc);
138
+
139
+ #define EVENT_SETUP \
140
+ debug_context_t *dc; \
141
+ VALUE context; \
142
+ rb_trace_arg_t *trace_arg; \
143
+ \
144
+ UNUSED(data); \
145
+ \
146
+ if (!is_living_thread(rb_thread_current())) \
147
+ return; \
148
+ \
149
+ thread_context_lookup(rb_thread_current(), &context); \
150
+ Data_Get_Struct(context, debug_context_t, dc); \
151
+ \
152
+ trace_arg = rb_tracearg_from_tracepoint(trace_point); \
153
+ if (verbose == Qtrue) \
154
+ trace_print(trace_arg, dc, 0, 0); \
155
+ \
156
+ if (CTX_FL_TEST(dc, CTX_FL_IGNORE)) \
157
+ return; \
158
+ \
159
+ acquire_lock(dc);
160
+
161
+
162
+ #define CALL_EVENT_SETUP \
163
+ dc->calced_stack_size++; \
164
+ dc->steps_out = dc->steps_out < 0 ? -1 : dc->steps_out + 1;
165
+
166
+ #define RETURN_EVENT_SETUP \
167
+ dc->calced_stack_size--; \
168
+ \
169
+ if (dc->steps_out == 1) \
170
+ dc->steps = 1;
171
+
172
+ #define RETURN_EVENT_TEARDOWN \
173
+ dc->steps_out = dc->steps_out <= 0 ? -1 : dc->steps_out - 1;
174
+
175
+
176
+ /* Functions that return control to byebug after the different events */
177
+
178
+ static VALUE
179
+ call_at(VALUE ctx, debug_context_t *dc, ID mid, int argc, VALUE arg)
180
+ {
181
+ struct call_with_inspection_data cwi;
182
+ VALUE argv[1];
183
+
184
+ argv[0] = arg;
185
+
186
+ cwi.dc = dc;
187
+ cwi.ctx = ctx;
188
+ cwi.id = mid;
189
+ cwi.argc = argc;
190
+ cwi.argv = &argv[0];
191
+
192
+ return call_with_debug_inspector(&cwi);
193
+ }
194
+
195
+ static VALUE
196
+ call_at_line(VALUE ctx, debug_context_t *dc)
197
+ {
198
+ return call_at(ctx, dc, rb_intern("at_line"), 0, Qnil);
199
+ }
200
+
201
+ static VALUE
202
+ call_at_tracing(VALUE ctx, debug_context_t *dc)
203
+ {
204
+ return call_at(ctx, dc, rb_intern("at_tracing"), 0, Qnil);
205
+ }
206
+
207
+ static VALUE
208
+ call_at_breakpoint(VALUE ctx, debug_context_t *dc, VALUE breakpoint)
209
+ {
210
+ dc->stop_reason = CTX_STOP_BREAKPOINT;
211
+
212
+ return call_at(ctx, dc, rb_intern("at_breakpoint"), 1, breakpoint);
213
+ }
214
+
215
+ static VALUE
216
+ call_at_catchpoint(VALUE ctx, debug_context_t *dc, VALUE exp)
217
+ {
218
+ dc->stop_reason = CTX_STOP_CATCHPOINT;
219
+
220
+ return call_at(ctx, dc, rb_intern("at_catchpoint"), 1, exp);
221
+ }
222
+
223
+ static VALUE
224
+ call_at_return(VALUE ctx, debug_context_t *dc, VALUE return_value)
225
+ {
226
+ dc->stop_reason = CTX_STOP_BREAKPOINT;
227
+
228
+ return call_at(ctx, dc, rb_intern("at_return"), 1, return_value);
229
+ }
230
+
231
+ static VALUE
232
+ call_at_end(VALUE ctx, debug_context_t *dc)
233
+ {
234
+ dc->stop_reason = CTX_STOP_BREAKPOINT;
235
+
236
+ return call_at(ctx, dc, rb_intern("at_end"), 0, Qnil);
237
+ }
238
+
239
+ static void
240
+ call_at_line_check(VALUE ctx, debug_context_t *dc, VALUE breakpoint)
241
+ {
242
+ dc->stop_reason = CTX_STOP_STEP;
243
+
244
+ if (!NIL_P(breakpoint))
245
+ call_at_breakpoint(ctx, dc, breakpoint);
246
+
247
+ byebug_reset_stepping_stop_points(dc);
248
+
249
+ call_at_line(ctx, dc);
250
+ }
251
+
252
+
253
+ /* TracePoint API event handlers */
254
+
255
+ static void
256
+ line_event(VALUE trace_point, void *data)
257
+ {
258
+ VALUE brkpnt, file, line, binding;
259
+
260
+ EVENT_SETUP;
261
+
262
+ file = rb_tracearg_path(trace_arg);
263
+ line = rb_tracearg_lineno(trace_arg);
264
+ binding = rb_tracearg_binding(trace_arg);
265
+
266
+ if (RTEST(tracing))
267
+ call_at_tracing(context, dc);
268
+
269
+ if (!CTX_FL_TEST(dc, CTX_FL_IGNORE_STEPS))
270
+ dc->steps = dc->steps <= 0 ? -1 : dc->steps - 1;
271
+
272
+ if (dc->calced_stack_size <= dc->dest_frame)
273
+ {
274
+ dc->dest_frame = dc->calced_stack_size;
275
+ CTX_FL_UNSET(dc, CTX_FL_IGNORE_STEPS);
276
+
277
+ dc->lines = dc->lines <= 0 ? -1 : dc->lines - 1;
278
+ }
279
+
280
+ if (dc->steps == 0 || dc->lines == 0)
281
+ call_at_line_check(context, dc, Qnil);
282
+ else
283
+ {
284
+ brkpnt = Qnil;
285
+
286
+ if (!NIL_P(breakpoints))
287
+ brkpnt = find_breakpoint_by_pos(breakpoints, file, line, binding);
288
+
289
+ if (!NIL_P(brkpnt))
290
+ call_at_line_check(context, dc, brkpnt);
291
+ }
292
+
293
+ EVENT_TEARDOWN;
294
+ }
295
+
296
+ static void
297
+ call_event(VALUE trace_point, void *data)
298
+ {
299
+ VALUE brkpnt, klass, msym, mid, binding, self;
300
+
301
+ EVENT_SETUP;
302
+
303
+ if (dc->calced_stack_size <= dc->dest_frame)
304
+ CTX_FL_UNSET(dc, CTX_FL_IGNORE_STEPS);
305
+
306
+ CALL_EVENT_SETUP;
307
+
308
+ /* nil method_id means we are at top level so there can't be a method
309
+ * breakpoint here. Just leave then. */
310
+ msym = rb_tracearg_method_id(trace_arg);
311
+ if (NIL_P(msym))
312
+ {
313
+ EVENT_TEARDOWN;
314
+ return;
315
+ }
316
+
317
+ mid = SYM2ID(msym);
318
+ klass = rb_tracearg_defined_class(trace_arg);
319
+ binding = rb_tracearg_binding(trace_arg);
320
+ self = rb_tracearg_self(trace_arg);
321
+
322
+ brkpnt = Qnil;
323
+
324
+ if (!NIL_P(breakpoints))
325
+ brkpnt = find_breakpoint_by_method(breakpoints, klass, mid, binding, self);
326
+
327
+ if (!NIL_P(brkpnt))
328
+ {
329
+ call_at_breakpoint(context, dc, brkpnt);
330
+ call_at_line(context, dc);
331
+ }
332
+
333
+ EVENT_TEARDOWN;
334
+ }
335
+
336
+ static void
337
+ return_event(VALUE trace_point, void *data)
338
+ {
339
+ VALUE brkpnt, file, line, binding;
340
+
341
+ EVENT_SETUP;
342
+
343
+ RETURN_EVENT_SETUP;
344
+
345
+ if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET)))
346
+ {
347
+ byebug_reset_stepping_stop_points(dc);
348
+
349
+ call_at_return(context, dc, rb_tracearg_return_value(trace_arg));
350
+ }
351
+ else if (!NIL_P(breakpoints))
352
+ {
353
+ file = rb_tracearg_path(trace_arg);
354
+ /*
355
+ * @todo Sometimes the TracePoint API gives some return events without
356
+ * file:line information, so we need to guard for nil until we know what's
357
+ * going on. This happens, for example, with active_support core extensions:
358
+ *
359
+ * [#7] call@.../core_ext/numeric/conversions.rb:124 Fixnum#to_s
360
+ * [#7] b_call@.../core_ext/numeric/conversions.rb:124 BigDecimal#to_s
361
+ * [#7] line@.../core_ext/numeric/conversions.rb:125 BigDecimal#to_s
362
+ * [#7] c_call@.../core_ext/numeric/conversions.rb:125 Kernel#is_a?
363
+ * [#7] c_return@.../core_ext/numeric/conversions.rb:125 Kernel#is_a?
364
+ * [#7] line@.../core_ext/numeric/conversions.rb:131 BigDecimal#to_s
365
+ * [#7] c_call@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s
366
+ * [#7] c_return@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s
367
+ * [#7] b_return@/hort/core_ext/numeric/conversions.rb:133 BigDecimal#to_s
368
+ * [#7] return@:0 Fixnum#to_s # => This guy...
369
+ */
370
+ if (!NIL_P(file))
371
+ {
372
+ line = rb_tracearg_lineno(trace_arg);
373
+ binding = rb_tracearg_binding(trace_arg);
374
+
375
+ brkpnt = find_breakpoint_by_pos(breakpoints, file, line, binding);
376
+
377
+ if (!NIL_P(brkpnt))
378
+ call_at_return(context, dc, rb_tracearg_return_value(trace_arg));
379
+ }
380
+ }
381
+
382
+ RETURN_EVENT_TEARDOWN;
383
+
384
+ EVENT_TEARDOWN;
385
+ }
386
+
387
+ static void
388
+ end_event(VALUE trace_point, void *data)
389
+ {
390
+ EVENT_SETUP;
391
+
392
+ RETURN_EVENT_SETUP;
393
+
394
+ if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET)))
395
+ {
396
+ byebug_reset_stepping_stop_points(dc);
397
+
398
+ call_at_end(context, dc);
399
+ }
400
+
401
+ RETURN_EVENT_TEARDOWN;
402
+
403
+ EVENT_TEARDOWN;
404
+ }
405
+
406
+ static void
407
+ raw_call_event(VALUE trace_point, void *data)
408
+ {
409
+ EVENT_SETUP;
410
+
411
+ CALL_EVENT_SETUP;
412
+
413
+ EVENT_TEARDOWN;
414
+ }
415
+
416
+ static void
417
+ raw_return_event(VALUE trace_point, void *data)
418
+ {
419
+ EVENT_SETUP;
420
+
421
+ RETURN_EVENT_SETUP;
422
+
423
+ RETURN_EVENT_TEARDOWN;
424
+
425
+ EVENT_TEARDOWN;
426
+ }
427
+
428
+ static void
429
+ raise_event(VALUE trace_point, void *data)
430
+ {
431
+ VALUE expn_class, ancestors, pm_context;
432
+ int i;
433
+ debug_context_t *new_dc;
434
+
435
+ EVENT_SETUP;
436
+
437
+ raised_exception = rb_tracearg_raised_exception(trace_arg);
438
+
439
+ if (post_mortem == Qtrue)
440
+ {
441
+ pm_context = context_dup(dc);
442
+ rb_ivar_set(raised_exception, rb_intern("@__bb_context"), pm_context);
443
+
444
+ Data_Get_Struct(pm_context, debug_context_t, new_dc);
445
+ rb_debug_inspector_open(context_backtrace_set, (void *)new_dc);
446
+ }
447
+
448
+ if (NIL_P(catchpoints) || dc->calced_stack_size == 0
449
+ || RHASH_TBL(catchpoints)->num_entries == 0)
450
+ {
451
+ EVENT_TEARDOWN;
452
+ return;
453
+ }
454
+
455
+ expn_class = rb_obj_class(raised_exception);
456
+ ancestors = rb_mod_ancestors(expn_class);
457
+ for (i = 0; i < RARRAY_LENINT(ancestors); i++)
458
+ {
459
+ VALUE ancestor_class, module_name, hit_count;
460
+
461
+ ancestor_class = rb_ary_entry(ancestors, i);
462
+ module_name = rb_mod_name(ancestor_class);
463
+ hit_count = rb_hash_aref(catchpoints, module_name);
464
+
465
+ /* increment exception */
466
+ if (!NIL_P(hit_count))
467
+ {
468
+ rb_hash_aset(catchpoints, module_name, INT2FIX(FIX2INT(hit_count) + 1));
469
+
470
+ call_at_catchpoint(context, dc, raised_exception);
471
+ call_at_line(context, dc);
472
+
473
+ break;
474
+ }
475
+ }
476
+
477
+ EVENT_TEARDOWN;
478
+ }
479
+
480
+
481
+ /* Setup TracePoint functionality */
482
+
483
+ static void
484
+ register_tracepoints(VALUE self)
485
+ {
486
+ int i;
487
+ VALUE traces = tracepoints;
488
+
489
+ UNUSED(self);
490
+
491
+ if (NIL_P(traces))
492
+ {
493
+ int line_msk = RUBY_EVENT_LINE;
494
+ int call_msk = RUBY_EVENT_CALL;
495
+ int ret_msk = RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN;
496
+ int end_msk = RUBY_EVENT_END;
497
+ int raw_call_msk = RUBY_EVENT_C_CALL | RUBY_EVENT_B_CALL | RUBY_EVENT_CLASS;
498
+ int raw_ret_msk = RUBY_EVENT_C_RETURN;
499
+ int raise_msk = RUBY_EVENT_RAISE;
500
+
501
+ VALUE tpLine = rb_tracepoint_new(Qnil, line_msk, line_event, 0);
502
+ VALUE tpCall = rb_tracepoint_new(Qnil, call_msk, call_event, 0);
503
+ VALUE tpReturn = rb_tracepoint_new(Qnil, ret_msk, return_event, 0);
504
+ VALUE tpEnd = rb_tracepoint_new(Qnil, end_msk, end_event, 0);
505
+ VALUE tpCCall = rb_tracepoint_new(Qnil, raw_call_msk, raw_call_event, 0);
506
+ VALUE tpCReturn = rb_tracepoint_new(Qnil, raw_ret_msk, raw_return_event, 0);
507
+ VALUE tpRaise = rb_tracepoint_new(Qnil, raise_msk, raise_event, 0);
508
+
509
+ traces = rb_ary_new();
510
+ rb_ary_push(traces, tpLine);
511
+ rb_ary_push(traces, tpCall);
512
+ rb_ary_push(traces, tpReturn);
513
+ rb_ary_push(traces, tpEnd);
514
+ rb_ary_push(traces, tpCCall);
515
+ rb_ary_push(traces, tpCReturn);
516
+ rb_ary_push(traces, tpRaise);
517
+
518
+ tracepoints = traces;
519
+ }
520
+
521
+ for (i = 0; i < RARRAY_LENINT(traces); i++)
522
+ rb_tracepoint_enable(rb_ary_entry(traces, i));
523
+ }
524
+
525
+ static void
526
+ clear_tracepoints(VALUE self)
527
+ {
528
+ int i;
529
+
530
+ UNUSED(self);
531
+
532
+ for (i = RARRAY_LENINT(tracepoints) - 1; i >= 0; i--)
533
+ rb_tracepoint_disable(rb_ary_entry(tracepoints, i));
534
+ }
535
+
536
+
537
+ /* Byebug's Public API */
538
+
539
+ /*
540
+ * call-seq:
541
+ * Byebug.contexts -> array
542
+ *
543
+ * Returns an array of all contexts.
544
+ */
545
+ static VALUE
546
+ Contexts(VALUE self)
547
+ {
548
+ volatile VALUE list;
549
+ volatile VALUE new_list;
550
+ VALUE context;
551
+ threads_table_t *t_tbl;
552
+ debug_context_t *dc;
553
+ int i;
554
+
555
+ UNUSED(self);
556
+
557
+ check_started();
558
+
559
+ new_list = rb_ary_new();
560
+ list = rb_funcall(rb_cThread, rb_intern("list"), 0);
561
+
562
+ for (i = 0; i < RARRAY_LENINT(list); i++)
563
+ {
564
+ VALUE thread = rb_ary_entry(list, i);
565
+
566
+ thread_context_lookup(thread, &context);
567
+ rb_ary_push(new_list, context);
568
+ }
569
+
570
+ Data_Get_Struct(threads, threads_table_t, t_tbl);
571
+ st_clear(t_tbl->tbl);
572
+
573
+ for (i = 0; i < RARRAY_LENINT(new_list); i++)
574
+ {
575
+ context = rb_ary_entry(new_list, i);
576
+ Data_Get_Struct(context, debug_context_t, dc);
577
+ st_insert(t_tbl->tbl, dc->thread, context);
578
+ }
579
+
580
+ return new_list;
581
+ }
582
+
583
+ /*
584
+ * call-seq:
585
+ * Byebug.thread_context(thread) -> context
586
+ *
587
+ * Returns context of the thread passed as an argument.
588
+ */
589
+ static VALUE
590
+ Thread_context(VALUE self, VALUE thread)
591
+ {
592
+ VALUE context;
593
+
594
+ UNUSED(self);
595
+
596
+ check_started();
597
+
598
+ thread_context_lookup(thread, &context);
599
+
600
+ return context;
601
+ }
602
+
603
+ /*
604
+ * call-seq:
605
+ * Byebug.current_context -> context
606
+ *
607
+ * Returns the current context.
608
+ * <i>Note:</i> Byebug.current_context.thread == Thread.current
609
+ */
610
+ static VALUE
611
+ Current_context(VALUE self)
612
+ {
613
+ VALUE context;
614
+
615
+ UNUSED(self);
616
+
617
+ thread_context_lookup(rb_thread_current(), &context);
618
+
619
+ return context;
620
+ }
621
+
622
+ /*
623
+ * call-seq:
624
+ * Byebug.started? -> bool
625
+ *
626
+ * Returns +true+ byebug is started.
627
+ */
628
+ static VALUE
629
+ Started(VALUE self)
630
+ {
631
+ UNUSED(self);
632
+
633
+ return IS_STARTED ? Qtrue : Qfalse;
634
+ }
635
+
636
+ /*
637
+ * call-seq:
638
+ * Byebug.stop -> bool
639
+ *
640
+ * This method disables byebug. It returns +true+ if byebug was already
641
+ * disabled, otherwise it returns +false+.
642
+ */
643
+ static VALUE
644
+ Stop(VALUE self)
645
+ {
646
+ UNUSED(self);
647
+
648
+ if (IS_STARTED)
649
+ {
650
+ clear_tracepoints(self);
651
+
652
+ breakpoints = Qnil;
653
+ catchpoints = Qnil;
654
+
655
+ return Qfalse;
656
+ }
657
+
658
+ return Qtrue;
659
+ }
660
+
661
+ static VALUE
662
+ Stoppable(VALUE self)
663
+ {
664
+ VALUE context;
665
+ debug_context_t *dc;
666
+
667
+ if (!IS_STARTED)
668
+ return Qfalse;
669
+
670
+ if (!NIL_P(breakpoints) && rb_funcall(breakpoints, idEmpty, 0) == Qfalse)
671
+ return Qfalse;
672
+
673
+ if (!NIL_P(catchpoints) && rb_funcall(catchpoints, idEmpty, 0) == Qfalse)
674
+ return Qfalse;
675
+
676
+ if (post_mortem == Qtrue)
677
+ return Qfalse;
678
+
679
+ if (RTEST(tracing))
680
+ return Qfalse;
681
+
682
+ context = Current_context(self);
683
+ if (!NIL_P(context))
684
+ {
685
+ Data_Get_Struct(context, debug_context_t, dc);
686
+
687
+ if (dc->steps > 0)
688
+ return Qfalse;
689
+ }
690
+
691
+ return Qtrue;
692
+ }
693
+
694
+ /*
695
+ * call-seq:
696
+ * Byebug.start -> bool
697
+ *
698
+ * The return value is the value of !Byebug.started? <i>before</i> issuing the
699
+ * +start+; That is, +true+ is returned, unless byebug was previously started.
700
+ */
701
+ static VALUE
702
+ Start(VALUE self)
703
+ {
704
+ if (IS_STARTED)
705
+ return Qfalse;
706
+
707
+ catchpoints = rb_hash_new();
708
+
709
+ threads = create_threads_table();
710
+
711
+ register_tracepoints(self);
712
+
713
+ return Qtrue;
714
+ }
715
+
716
+ /*
717
+ * call-seq:
718
+ * Byebug.debug_load(file, stop = false) -> nil
719
+ *
720
+ * Same as Kernel#load but resets current context's frames.
721
+ * +stop+ parameter forces byebug to stop at the first line of code in +file+
722
+ */
723
+ static VALUE
724
+ Debug_load(int argc, VALUE *argv, VALUE self)
725
+ {
726
+ VALUE file, stop, context;
727
+ debug_context_t *dc;
728
+ VALUE status = Qnil;
729
+ int state = 0;
730
+
731
+ UNUSED(self);
732
+
733
+ if (rb_scan_args(argc, argv, "11", &file, &stop) == 1)
734
+ stop = Qfalse;
735
+
736
+ Start(self);
737
+
738
+ context = Current_context(self);
739
+ Data_Get_Struct(context, debug_context_t, dc);
740
+
741
+ dc->calced_stack_size = 1;
742
+
743
+ if (RTEST(stop))
744
+ dc->steps = 1;
745
+
746
+ rb_load_protect(file, 0, &state);
747
+ if (0 != state)
748
+ {
749
+ status = rb_errinfo();
750
+ byebug_reset_stepping_stop_points(dc);
751
+ }
752
+
753
+ return status;
754
+ }
755
+
756
+ /*
757
+ * call-seq:
758
+ * Byebug.tracing? -> bool
759
+ *
760
+ * Returns +true+ if global tracing is enabled.
761
+ */
762
+ static VALUE
763
+ Tracing(VALUE self)
764
+ {
765
+ UNUSED(self);
766
+
767
+ return tracing;
768
+ }
769
+
770
+ /*
771
+ * call-seq:
772
+ * Byebug.tracing = bool
773
+ *
774
+ * Sets the global tracing flag.
775
+ */
776
+ static VALUE
777
+ Set_tracing(VALUE self, VALUE value)
778
+ {
779
+ UNUSED(self);
780
+
781
+ tracing = RTEST(value) ? Qtrue : Qfalse;
782
+ return value;
783
+ }
784
+
785
+ /*
786
+ * call-seq:
787
+ * Byebug.verbose? -> bool
788
+ *
789
+ * Returns +true+ if global verbose flag for TracePoint API events is enabled.
790
+ */
791
+ static VALUE
792
+ Verbose(VALUE self)
793
+ {
794
+ UNUSED(self);
795
+
796
+ return verbose;
797
+ }
798
+
799
+ /*
800
+ * call-seq:
801
+ * Byebug.verbose = bool
802
+ *
803
+ * Sets the global verbose flag for TracePoint API events is enabled.
804
+ */
805
+ static VALUE
806
+ Set_verbose(VALUE self, VALUE value)
807
+ {
808
+ UNUSED(self);
809
+
810
+ verbose = RTEST(value) ? Qtrue : Qfalse;
811
+ return value;
812
+ }
813
+
814
+ /*
815
+ * call-seq:
816
+ * Byebug.post_mortem? -> bool
817
+ *
818
+ * Returns +true+ if post-mortem debugging is enabled.
819
+ */
820
+ static VALUE
821
+ Post_mortem(VALUE self)
822
+ {
823
+ UNUSED(self);
824
+
825
+ return post_mortem;
826
+ }
827
+
828
+ /*
829
+ * call-seq:
830
+ * Byebug.post_mortem = bool
831
+ *
832
+ * Sets post-moterm flag.
833
+ */
834
+ static VALUE
835
+ Set_post_mortem(VALUE self, VALUE value)
836
+ {
837
+ UNUSED(self);
838
+
839
+ post_mortem = RTEST(value) ? Qtrue : Qfalse;
840
+ return value;
841
+ }
842
+
843
+ /*
844
+ * call-seq:
845
+ * Byebug.add_catchpoint(exception) -> exception
846
+ *
847
+ * Adds a new exception to the catchpoints hash.
848
+ */
849
+ static VALUE
850
+ Add_catchpoint(VALUE self, VALUE value)
851
+ {
852
+ UNUSED(self);
853
+
854
+ if (TYPE(value) != T_STRING)
855
+ rb_raise(rb_eTypeError, "value of a catchpoint must be String");
856
+
857
+ rb_hash_aset(catchpoints, rb_str_dup(value), INT2FIX(0));
858
+ return value;
859
+ }
860
+
861
+ /*
862
+ * Document-class: Byebug
863
+ *
864
+ * == Summary
865
+ *
866
+ * This is a singleton class allows controlling byebug. Use it to start/stop
867
+ * byebug, set/remove breakpoints, etc.
868
+ */
869
+ void
870
+ Init_byebug()
871
+ {
872
+ mByebug = rb_define_module("Byebug");
873
+
874
+ rb_define_module_function(mByebug, "add_catchpoint", Add_catchpoint, 1);
875
+ rb_define_module_function(mByebug, "breakpoints", Breakpoints, 0);
876
+ rb_define_module_function(mByebug, "catchpoints", Catchpoints, 0);
877
+ rb_define_module_function(mByebug, "contexts", Contexts, 0);
878
+ rb_define_module_function(mByebug, "current_context", Current_context, 0);
879
+ rb_define_module_function(mByebug, "debug_load", Debug_load, -1);
880
+ rb_define_module_function(mByebug, "post_mortem?", Post_mortem, 0);
881
+ rb_define_module_function(mByebug, "post_mortem=", Set_post_mortem, 1);
882
+ rb_define_module_function(mByebug, "raised_exception", Raised_exception, 0);
883
+ rb_define_module_function(mByebug, "start", Start, 0);
884
+ rb_define_module_function(mByebug, "started?", Started, 0);
885
+ rb_define_module_function(mByebug, "stop", Stop, 0);
886
+ rb_define_module_function(mByebug, "stoppable?", Stoppable, 0);
887
+ rb_define_module_function(mByebug, "thread_context", Thread_context, 1);
888
+ rb_define_module_function(mByebug, "tracing?", Tracing, 0);
889
+ rb_define_module_function(mByebug, "tracing=", Set_tracing, 1);
890
+ rb_define_module_function(mByebug, "verbose?", Verbose, 0);
891
+ rb_define_module_function(mByebug, "verbose=", Set_verbose, 1);
892
+
893
+ Init_threads_table(mByebug);
894
+ Init_byebug_context(mByebug);
895
+ Init_byebug_breakpoint(mByebug);
896
+
897
+ rb_global_variable(&breakpoints);
898
+ rb_global_variable(&catchpoints);
899
+ rb_global_variable(&tracepoints);
900
+ rb_global_variable(&raised_exception);
901
+ rb_global_variable(&threads);
902
+
903
+ idPuts = rb_intern("puts");
904
+ idEmpty = rb_intern("empty?");
905
+ }