runger_byebug 11.2.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 (132) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +954 -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 +521 -0
  9. data/ext/byebug/byebug.c +900 -0
  10. data/ext/byebug/byebug.h +145 -0
  11. data/ext/byebug/context.c +687 -0
  12. data/ext/byebug/extconf.rb +12 -0
  13. data/ext/byebug/locker.c +96 -0
  14. data/ext/byebug/threads.c +241 -0
  15. data/lib/byebug/attacher.rb +48 -0
  16. data/lib/byebug/breakpoint.rb +94 -0
  17. data/lib/byebug/command.rb +111 -0
  18. data/lib/byebug/command_list.rb +34 -0
  19. data/lib/byebug/commands/break.rb +114 -0
  20. data/lib/byebug/commands/catch.rb +78 -0
  21. data/lib/byebug/commands/condition.rb +55 -0
  22. data/lib/byebug/commands/continue.rb +68 -0
  23. data/lib/byebug/commands/debug.rb +38 -0
  24. data/lib/byebug/commands/delete.rb +55 -0
  25. data/lib/byebug/commands/disable/breakpoints.rb +42 -0
  26. data/lib/byebug/commands/disable/display.rb +43 -0
  27. data/lib/byebug/commands/disable.rb +33 -0
  28. data/lib/byebug/commands/display.rb +66 -0
  29. data/lib/byebug/commands/down.rb +45 -0
  30. data/lib/byebug/commands/edit.rb +69 -0
  31. data/lib/byebug/commands/enable/breakpoints.rb +42 -0
  32. data/lib/byebug/commands/enable/display.rb +43 -0
  33. data/lib/byebug/commands/enable.rb +33 -0
  34. data/lib/byebug/commands/finish.rb +57 -0
  35. data/lib/byebug/commands/frame.rb +57 -0
  36. data/lib/byebug/commands/help.rb +64 -0
  37. data/lib/byebug/commands/history.rb +39 -0
  38. data/lib/byebug/commands/info/breakpoints.rb +65 -0
  39. data/lib/byebug/commands/info/display.rb +49 -0
  40. data/lib/byebug/commands/info/file.rb +80 -0
  41. data/lib/byebug/commands/info/line.rb +35 -0
  42. data/lib/byebug/commands/info/program.rb +49 -0
  43. data/lib/byebug/commands/info.rb +37 -0
  44. data/lib/byebug/commands/interrupt.rb +34 -0
  45. data/lib/byebug/commands/irb.rb +50 -0
  46. data/lib/byebug/commands/kill.rb +45 -0
  47. data/lib/byebug/commands/list.rb +159 -0
  48. data/lib/byebug/commands/method.rb +53 -0
  49. data/lib/byebug/commands/next.rb +40 -0
  50. data/lib/byebug/commands/pry.rb +41 -0
  51. data/lib/byebug/commands/quit.rb +42 -0
  52. data/lib/byebug/commands/restart.rb +64 -0
  53. data/lib/byebug/commands/save.rb +72 -0
  54. data/lib/byebug/commands/set.rb +79 -0
  55. data/lib/byebug/commands/show.rb +45 -0
  56. data/lib/byebug/commands/skip.rb +85 -0
  57. data/lib/byebug/commands/source.rb +40 -0
  58. data/lib/byebug/commands/step.rb +40 -0
  59. data/lib/byebug/commands/thread/current.rb +37 -0
  60. data/lib/byebug/commands/thread/list.rb +43 -0
  61. data/lib/byebug/commands/thread/resume.rb +45 -0
  62. data/lib/byebug/commands/thread/stop.rb +43 -0
  63. data/lib/byebug/commands/thread/switch.rb +46 -0
  64. data/lib/byebug/commands/thread.rb +34 -0
  65. data/lib/byebug/commands/tracevar.rb +54 -0
  66. data/lib/byebug/commands/undisplay.rb +51 -0
  67. data/lib/byebug/commands/untracevar.rb +36 -0
  68. data/lib/byebug/commands/up.rb +45 -0
  69. data/lib/byebug/commands/var/all.rb +41 -0
  70. data/lib/byebug/commands/var/args.rb +39 -0
  71. data/lib/byebug/commands/var/const.rb +49 -0
  72. data/lib/byebug/commands/var/global.rb +37 -0
  73. data/lib/byebug/commands/var/instance.rb +39 -0
  74. data/lib/byebug/commands/var/local.rb +39 -0
  75. data/lib/byebug/commands/var.rb +37 -0
  76. data/lib/byebug/commands/where.rb +64 -0
  77. data/lib/byebug/commands.rb +40 -0
  78. data/lib/byebug/context.rb +157 -0
  79. data/lib/byebug/core.rb +115 -0
  80. data/lib/byebug/errors.rb +29 -0
  81. data/lib/byebug/frame.rb +185 -0
  82. data/lib/byebug/helpers/bin.rb +47 -0
  83. data/lib/byebug/helpers/eval.rb +134 -0
  84. data/lib/byebug/helpers/file.rb +63 -0
  85. data/lib/byebug/helpers/frame.rb +75 -0
  86. data/lib/byebug/helpers/parse.rb +80 -0
  87. data/lib/byebug/helpers/path.rb +40 -0
  88. data/lib/byebug/helpers/reflection.rb +19 -0
  89. data/lib/byebug/helpers/string.rb +33 -0
  90. data/lib/byebug/helpers/thread.rb +67 -0
  91. data/lib/byebug/helpers/toggle.rb +62 -0
  92. data/lib/byebug/helpers/var.rb +70 -0
  93. data/lib/byebug/history.rb +130 -0
  94. data/lib/byebug/interface.rb +146 -0
  95. data/lib/byebug/interfaces/local_interface.rb +63 -0
  96. data/lib/byebug/interfaces/remote_interface.rb +50 -0
  97. data/lib/byebug/interfaces/script_interface.rb +33 -0
  98. data/lib/byebug/interfaces/test_interface.rb +67 -0
  99. data/lib/byebug/option_setter.rb +95 -0
  100. data/lib/byebug/printers/base.rb +68 -0
  101. data/lib/byebug/printers/plain.rb +44 -0
  102. data/lib/byebug/printers/texts/base.yml +115 -0
  103. data/lib/byebug/printers/texts/plain.yml +33 -0
  104. data/lib/byebug/processors/command_processor.rb +173 -0
  105. data/lib/byebug/processors/control_processor.rb +24 -0
  106. data/lib/byebug/processors/post_mortem_processor.rb +18 -0
  107. data/lib/byebug/processors/script_processor.rb +49 -0
  108. data/lib/byebug/remote/client.rb +57 -0
  109. data/lib/byebug/remote/server.rb +47 -0
  110. data/lib/byebug/remote.rb +85 -0
  111. data/lib/byebug/runner.rb +198 -0
  112. data/lib/byebug/setting.rb +79 -0
  113. data/lib/byebug/settings/autoirb.rb +29 -0
  114. data/lib/byebug/settings/autolist.rb +29 -0
  115. data/lib/byebug/settings/autopry.rb +29 -0
  116. data/lib/byebug/settings/autosave.rb +17 -0
  117. data/lib/byebug/settings/basename.rb +16 -0
  118. data/lib/byebug/settings/callstyle.rb +20 -0
  119. data/lib/byebug/settings/fullpath.rb +16 -0
  120. data/lib/byebug/settings/histfile.rb +20 -0
  121. data/lib/byebug/settings/histsize.rb +20 -0
  122. data/lib/byebug/settings/linetrace.rb +22 -0
  123. data/lib/byebug/settings/listsize.rb +21 -0
  124. data/lib/byebug/settings/post_mortem.rb +27 -0
  125. data/lib/byebug/settings/savefile.rb +20 -0
  126. data/lib/byebug/settings/stack_on_error.rb +15 -0
  127. data/lib/byebug/settings/width.rb +20 -0
  128. data/lib/byebug/source_file_formatter.rb +71 -0
  129. data/lib/byebug/subcommands.rb +54 -0
  130. data/lib/byebug/version.rb +8 -0
  131. data/lib/byebug.rb +3 -0
  132. metadata +194 -0
@@ -0,0 +1,900 @@
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
+ msym = rb_tracearg_method_id(trace_arg);
309
+
310
+ mid = SYM2ID(msym);
311
+ klass = rb_tracearg_defined_class(trace_arg);
312
+ binding = rb_tracearg_binding(trace_arg);
313
+ self = rb_tracearg_self(trace_arg);
314
+
315
+ brkpnt = Qnil;
316
+
317
+ if (!NIL_P(breakpoints))
318
+ brkpnt = find_breakpoint_by_method(breakpoints, klass, mid, binding, self);
319
+
320
+ if (!NIL_P(brkpnt))
321
+ {
322
+ call_at_breakpoint(context, dc, brkpnt);
323
+ call_at_line(context, dc);
324
+ }
325
+
326
+ EVENT_TEARDOWN;
327
+ }
328
+
329
+ static void
330
+ return_event(VALUE trace_point, void *data)
331
+ {
332
+ VALUE brkpnt, file, line, binding;
333
+
334
+ EVENT_SETUP;
335
+
336
+ RETURN_EVENT_SETUP;
337
+
338
+ if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET)))
339
+ {
340
+ byebug_reset_stepping_stop_points(dc);
341
+
342
+ call_at_return(context, dc, rb_tracearg_return_value(trace_arg));
343
+ }
344
+ else if (!NIL_P(breakpoints))
345
+ {
346
+ file = rb_tracearg_path(trace_arg);
347
+ /*
348
+ * @todo Sometimes the TracePoint API gives some return events without
349
+ * file:line information, so we need to guard for nil until we know what's
350
+ * going on. This happens, for example, with active_support core extensions:
351
+ *
352
+ * [#7] call@.../core_ext/numeric/conversions.rb:124 Fixnum#to_s
353
+ * [#7] b_call@.../core_ext/numeric/conversions.rb:124 BigDecimal#to_s
354
+ * [#7] line@.../core_ext/numeric/conversions.rb:125 BigDecimal#to_s
355
+ * [#7] c_call@.../core_ext/numeric/conversions.rb:125 Kernel#is_a?
356
+ * [#7] c_return@.../core_ext/numeric/conversions.rb:125 Kernel#is_a?
357
+ * [#7] line@.../core_ext/numeric/conversions.rb:131 BigDecimal#to_s
358
+ * [#7] c_call@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s
359
+ * [#7] c_return@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s
360
+ * [#7] b_return@/hort/core_ext/numeric/conversions.rb:133 BigDecimal#to_s
361
+ * [#7] return@:0 Fixnum#to_s # => This guy...
362
+ */
363
+ if (NIL_P(file))
364
+ rb_warn("The TracePoint API emitted a return event without file information. It might be a bug, please report this.");
365
+ else
366
+ {
367
+ line = rb_tracearg_lineno(trace_arg);
368
+ binding = rb_tracearg_binding(trace_arg);
369
+
370
+ brkpnt = find_breakpoint_by_pos(breakpoints, file, line, binding);
371
+
372
+ if (!NIL_P(brkpnt))
373
+ call_at_return(context, dc, rb_tracearg_return_value(trace_arg));
374
+ }
375
+ }
376
+
377
+ RETURN_EVENT_TEARDOWN;
378
+
379
+ EVENT_TEARDOWN;
380
+ }
381
+
382
+ static void
383
+ end_event(VALUE trace_point, void *data)
384
+ {
385
+ EVENT_SETUP;
386
+
387
+ RETURN_EVENT_SETUP;
388
+
389
+ if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET)))
390
+ {
391
+ byebug_reset_stepping_stop_points(dc);
392
+
393
+ call_at_end(context, dc);
394
+ }
395
+
396
+ RETURN_EVENT_TEARDOWN;
397
+
398
+ EVENT_TEARDOWN;
399
+ }
400
+
401
+ static void
402
+ raw_call_event(VALUE trace_point, void *data)
403
+ {
404
+ EVENT_SETUP;
405
+
406
+ CALL_EVENT_SETUP;
407
+
408
+ EVENT_TEARDOWN;
409
+ }
410
+
411
+ static void
412
+ raw_return_event(VALUE trace_point, void *data)
413
+ {
414
+ EVENT_SETUP;
415
+
416
+ RETURN_EVENT_SETUP;
417
+
418
+ RETURN_EVENT_TEARDOWN;
419
+
420
+ EVENT_TEARDOWN;
421
+ }
422
+
423
+ static void
424
+ raise_event(VALUE trace_point, void *data)
425
+ {
426
+ VALUE expn_class, ancestors, pm_context;
427
+ int i;
428
+ debug_context_t *new_dc;
429
+
430
+ EVENT_SETUP;
431
+
432
+ raised_exception = rb_tracearg_raised_exception(trace_arg);
433
+
434
+ if (post_mortem == Qtrue && !rb_ivar_defined(raised_exception, rb_intern("@__bb_context")))
435
+ {
436
+ pm_context = context_dup(dc);
437
+ rb_ivar_set(raised_exception, rb_intern("@__bb_context"), pm_context);
438
+
439
+ Data_Get_Struct(pm_context, debug_context_t, new_dc);
440
+ rb_debug_inspector_open(context_backtrace_set, (void *)new_dc);
441
+ }
442
+
443
+ if (NIL_P(catchpoints) || dc->calced_stack_size == 0
444
+ || RHASH_TBL(catchpoints)->num_entries == 0)
445
+ {
446
+ EVENT_TEARDOWN;
447
+ return;
448
+ }
449
+
450
+ expn_class = rb_obj_class(raised_exception);
451
+ ancestors = rb_mod_ancestors(expn_class);
452
+ for (i = 0; i < RARRAY_LENINT(ancestors); i++)
453
+ {
454
+ VALUE ancestor_class, module_name, hit_count;
455
+
456
+ ancestor_class = rb_ary_entry(ancestors, i);
457
+ module_name = rb_mod_name(ancestor_class);
458
+ hit_count = rb_hash_aref(catchpoints, module_name);
459
+
460
+ /* increment exception */
461
+ if (!NIL_P(hit_count))
462
+ {
463
+ rb_hash_aset(catchpoints, module_name, INT2FIX(FIX2INT(hit_count) + 1));
464
+
465
+ call_at_catchpoint(context, dc, raised_exception);
466
+ call_at_line(context, dc);
467
+
468
+ break;
469
+ }
470
+ }
471
+
472
+ EVENT_TEARDOWN;
473
+ }
474
+
475
+
476
+ /* Setup TracePoint functionality */
477
+
478
+ static void
479
+ register_tracepoints(VALUE self)
480
+ {
481
+ int i;
482
+ VALUE traces = tracepoints;
483
+
484
+ UNUSED(self);
485
+
486
+ if (NIL_P(traces))
487
+ {
488
+ int line_msk = RUBY_EVENT_LINE;
489
+ int call_msk = RUBY_EVENT_CALL;
490
+ int ret_msk = RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN;
491
+ int end_msk = RUBY_EVENT_END;
492
+ int raw_call_msk = RUBY_EVENT_C_CALL | RUBY_EVENT_B_CALL | RUBY_EVENT_CLASS;
493
+ int raw_ret_msk = RUBY_EVENT_C_RETURN;
494
+ int raise_msk = RUBY_EVENT_RAISE;
495
+
496
+ VALUE tpLine = rb_tracepoint_new(Qnil, line_msk, line_event, 0);
497
+ VALUE tpCall = rb_tracepoint_new(Qnil, call_msk, call_event, 0);
498
+ VALUE tpReturn = rb_tracepoint_new(Qnil, ret_msk, return_event, 0);
499
+ VALUE tpEnd = rb_tracepoint_new(Qnil, end_msk, end_event, 0);
500
+ VALUE tpCCall = rb_tracepoint_new(Qnil, raw_call_msk, raw_call_event, 0);
501
+ VALUE tpCReturn = rb_tracepoint_new(Qnil, raw_ret_msk, raw_return_event, 0);
502
+ VALUE tpRaise = rb_tracepoint_new(Qnil, raise_msk, raise_event, 0);
503
+
504
+ traces = rb_ary_new();
505
+ rb_ary_push(traces, tpLine);
506
+ rb_ary_push(traces, tpCall);
507
+ rb_ary_push(traces, tpReturn);
508
+ rb_ary_push(traces, tpEnd);
509
+ rb_ary_push(traces, tpCCall);
510
+ rb_ary_push(traces, tpCReturn);
511
+ rb_ary_push(traces, tpRaise);
512
+
513
+ tracepoints = traces;
514
+ }
515
+
516
+ for (i = 0; i < RARRAY_LENINT(traces); i++)
517
+ rb_tracepoint_enable(rb_ary_entry(traces, i));
518
+ }
519
+
520
+ static void
521
+ clear_tracepoints(VALUE self)
522
+ {
523
+ int i;
524
+
525
+ UNUSED(self);
526
+
527
+ for (i = RARRAY_LENINT(tracepoints) - 1; i >= 0; i--)
528
+ rb_tracepoint_disable(rb_ary_entry(tracepoints, i));
529
+ }
530
+
531
+
532
+ /* Byebug's Public API */
533
+
534
+ /*
535
+ * call-seq:
536
+ * Byebug.contexts -> array
537
+ *
538
+ * Returns an array of all contexts.
539
+ */
540
+ static VALUE
541
+ Contexts(VALUE self)
542
+ {
543
+ volatile VALUE list;
544
+ volatile VALUE new_list;
545
+ VALUE context;
546
+ threads_table_t *t_tbl;
547
+ debug_context_t *dc;
548
+ int i;
549
+
550
+ UNUSED(self);
551
+
552
+ check_started();
553
+
554
+ new_list = rb_ary_new();
555
+ list = rb_funcall(rb_cThread, rb_intern("list"), 0);
556
+
557
+ for (i = 0; i < RARRAY_LENINT(list); i++)
558
+ {
559
+ VALUE thread = rb_ary_entry(list, i);
560
+
561
+ thread_context_lookup(thread, &context);
562
+ rb_ary_push(new_list, context);
563
+ }
564
+
565
+ Data_Get_Struct(threads, threads_table_t, t_tbl);
566
+ st_clear(t_tbl->tbl);
567
+
568
+ for (i = 0; i < RARRAY_LENINT(new_list); i++)
569
+ {
570
+ context = rb_ary_entry(new_list, i);
571
+ Data_Get_Struct(context, debug_context_t, dc);
572
+ st_insert(t_tbl->tbl, dc->thread, context);
573
+ }
574
+
575
+ return new_list;
576
+ }
577
+
578
+ /*
579
+ * call-seq:
580
+ * Byebug.thread_context(thread) -> context
581
+ *
582
+ * Returns context of the thread passed as an argument.
583
+ */
584
+ static VALUE
585
+ Thread_context(VALUE self, VALUE thread)
586
+ {
587
+ VALUE context;
588
+
589
+ UNUSED(self);
590
+
591
+ check_started();
592
+
593
+ thread_context_lookup(thread, &context);
594
+
595
+ return context;
596
+ }
597
+
598
+ /*
599
+ * call-seq:
600
+ * Byebug.current_context -> context
601
+ *
602
+ * Returns the current context.
603
+ * <i>Note:</i> Byebug.current_context.thread == Thread.current
604
+ */
605
+ static VALUE
606
+ Current_context(VALUE self)
607
+ {
608
+ VALUE context;
609
+
610
+ UNUSED(self);
611
+
612
+ thread_context_lookup(rb_thread_current(), &context);
613
+
614
+ return context;
615
+ }
616
+
617
+ /*
618
+ * call-seq:
619
+ * Byebug.started? -> bool
620
+ *
621
+ * Returns +true+ byebug is started.
622
+ */
623
+ static VALUE
624
+ Started(VALUE self)
625
+ {
626
+ UNUSED(self);
627
+
628
+ return IS_STARTED ? Qtrue : Qfalse;
629
+ }
630
+
631
+ /*
632
+ * call-seq:
633
+ * Byebug.stop -> bool
634
+ *
635
+ * This method disables byebug. It returns +true+ if byebug was already
636
+ * disabled, otherwise it returns +false+.
637
+ */
638
+ static VALUE
639
+ Stop(VALUE self)
640
+ {
641
+ UNUSED(self);
642
+
643
+ if (IS_STARTED)
644
+ {
645
+ clear_tracepoints(self);
646
+
647
+ breakpoints = Qnil;
648
+ catchpoints = Qnil;
649
+
650
+ return Qfalse;
651
+ }
652
+
653
+ return Qtrue;
654
+ }
655
+
656
+ static VALUE
657
+ Stoppable(VALUE self)
658
+ {
659
+ VALUE context;
660
+ debug_context_t *dc;
661
+
662
+ if (!IS_STARTED)
663
+ return Qfalse;
664
+
665
+ if (!NIL_P(breakpoints) && rb_funcall(breakpoints, idEmpty, 0) == Qfalse)
666
+ return Qfalse;
667
+
668
+ if (!NIL_P(catchpoints) && rb_funcall(catchpoints, idEmpty, 0) == Qfalse)
669
+ return Qfalse;
670
+
671
+ if (post_mortem == Qtrue)
672
+ return Qfalse;
673
+
674
+ if (RTEST(tracing))
675
+ return Qfalse;
676
+
677
+ context = Current_context(self);
678
+ if (!NIL_P(context))
679
+ {
680
+ Data_Get_Struct(context, debug_context_t, dc);
681
+
682
+ if (dc->steps > 0)
683
+ return Qfalse;
684
+ }
685
+
686
+ return Qtrue;
687
+ }
688
+
689
+ /*
690
+ * call-seq:
691
+ * Byebug.start -> bool
692
+ *
693
+ * The return value is the value of !Byebug.started? <i>before</i> issuing the
694
+ * +start+; That is, +true+ is returned, unless byebug was previously started.
695
+ */
696
+ static VALUE
697
+ Start(VALUE self)
698
+ {
699
+ if (IS_STARTED)
700
+ return Qfalse;
701
+
702
+ catchpoints = rb_hash_new();
703
+
704
+ threads = create_threads_table();
705
+
706
+ register_tracepoints(self);
707
+
708
+ return Qtrue;
709
+ }
710
+
711
+ /*
712
+ * call-seq:
713
+ * Byebug.debug_load(file, stop = false) -> nil
714
+ *
715
+ * Same as Kernel#load but resets current context's frames.
716
+ * +stop+ parameter forces byebug to stop at the first line of code in +file+
717
+ */
718
+ static VALUE
719
+ Debug_load(int argc, VALUE *argv, VALUE self)
720
+ {
721
+ VALUE file, stop, context;
722
+ debug_context_t *dc;
723
+ VALUE status = Qnil;
724
+ int state = 0;
725
+
726
+ UNUSED(self);
727
+
728
+ if (rb_scan_args(argc, argv, "11", &file, &stop) == 1)
729
+ stop = Qfalse;
730
+
731
+ Start(self);
732
+
733
+ context = Current_context(self);
734
+ Data_Get_Struct(context, debug_context_t, dc);
735
+
736
+ dc->calced_stack_size = 1;
737
+
738
+ if (RTEST(stop))
739
+ dc->steps = 1;
740
+
741
+ rb_load_protect(file, 0, &state);
742
+ if (0 != state)
743
+ {
744
+ status = rb_errinfo();
745
+ byebug_reset_stepping_stop_points(dc);
746
+ }
747
+
748
+ return status;
749
+ }
750
+
751
+ /*
752
+ * call-seq:
753
+ * Byebug.tracing? -> bool
754
+ *
755
+ * Returns +true+ if global tracing is enabled.
756
+ */
757
+ static VALUE
758
+ Tracing(VALUE self)
759
+ {
760
+ UNUSED(self);
761
+
762
+ return tracing;
763
+ }
764
+
765
+ /*
766
+ * call-seq:
767
+ * Byebug.tracing = bool
768
+ *
769
+ * Sets the global tracing flag.
770
+ */
771
+ static VALUE
772
+ Set_tracing(VALUE self, VALUE value)
773
+ {
774
+ UNUSED(self);
775
+
776
+ tracing = RTEST(value) ? Qtrue : Qfalse;
777
+ return value;
778
+ }
779
+
780
+ /*
781
+ * call-seq:
782
+ * Byebug.verbose? -> bool
783
+ *
784
+ * Returns +true+ if global verbose flag for TracePoint API events is enabled.
785
+ */
786
+ static VALUE
787
+ Verbose(VALUE self)
788
+ {
789
+ UNUSED(self);
790
+
791
+ return verbose;
792
+ }
793
+
794
+ /*
795
+ * call-seq:
796
+ * Byebug.verbose = bool
797
+ *
798
+ * Sets the global verbose flag for TracePoint API events is enabled.
799
+ */
800
+ static VALUE
801
+ Set_verbose(VALUE self, VALUE value)
802
+ {
803
+ UNUSED(self);
804
+
805
+ verbose = RTEST(value) ? Qtrue : Qfalse;
806
+ return value;
807
+ }
808
+
809
+ /*
810
+ * call-seq:
811
+ * Byebug.post_mortem? -> bool
812
+ *
813
+ * Returns +true+ if post-mortem debugging is enabled.
814
+ */
815
+ static VALUE
816
+ Post_mortem(VALUE self)
817
+ {
818
+ UNUSED(self);
819
+
820
+ return post_mortem;
821
+ }
822
+
823
+ /*
824
+ * call-seq:
825
+ * Byebug.post_mortem = bool
826
+ *
827
+ * Sets post-moterm flag.
828
+ */
829
+ static VALUE
830
+ Set_post_mortem(VALUE self, VALUE value)
831
+ {
832
+ UNUSED(self);
833
+
834
+ post_mortem = RTEST(value) ? Qtrue : Qfalse;
835
+ return value;
836
+ }
837
+
838
+ /*
839
+ * call-seq:
840
+ * Byebug.add_catchpoint(exception) -> exception
841
+ *
842
+ * Adds a new exception to the catchpoints hash.
843
+ */
844
+ static VALUE
845
+ Add_catchpoint(VALUE self, VALUE value)
846
+ {
847
+ UNUSED(self);
848
+
849
+ if (TYPE(value) != T_STRING)
850
+ rb_raise(rb_eTypeError, "value of a catchpoint must be String");
851
+
852
+ rb_hash_aset(catchpoints, rb_str_dup(value), INT2FIX(0));
853
+ return value;
854
+ }
855
+
856
+ /*
857
+ * Document-module: Byebug
858
+ *
859
+ * == Summary
860
+ *
861
+ * This is a singleton class allows controlling byebug. Use it to start/stop
862
+ * byebug, set/remove breakpoints, etc.
863
+ */
864
+ void
865
+ Init_byebug()
866
+ {
867
+ mByebug = rb_define_module("Byebug");
868
+
869
+ rb_define_module_function(mByebug, "add_catchpoint", Add_catchpoint, 1);
870
+ rb_define_module_function(mByebug, "breakpoints", Breakpoints, 0);
871
+ rb_define_module_function(mByebug, "catchpoints", Catchpoints, 0);
872
+ rb_define_module_function(mByebug, "contexts", Contexts, 0);
873
+ rb_define_module_function(mByebug, "current_context", Current_context, 0);
874
+ rb_define_module_function(mByebug, "debug_load", Debug_load, -1);
875
+ rb_define_module_function(mByebug, "post_mortem?", Post_mortem, 0);
876
+ rb_define_module_function(mByebug, "post_mortem=", Set_post_mortem, 1);
877
+ rb_define_module_function(mByebug, "raised_exception", Raised_exception, 0);
878
+ rb_define_module_function(mByebug, "start", Start, 0);
879
+ rb_define_module_function(mByebug, "started?", Started, 0);
880
+ rb_define_module_function(mByebug, "stop", Stop, 0);
881
+ rb_define_module_function(mByebug, "stoppable?", Stoppable, 0);
882
+ rb_define_module_function(mByebug, "thread_context", Thread_context, 1);
883
+ rb_define_module_function(mByebug, "tracing?", Tracing, 0);
884
+ rb_define_module_function(mByebug, "tracing=", Set_tracing, 1);
885
+ rb_define_module_function(mByebug, "verbose?", Verbose, 0);
886
+ rb_define_module_function(mByebug, "verbose=", Set_verbose, 1);
887
+
888
+ Init_threads_table(mByebug);
889
+ Init_byebug_context(mByebug);
890
+ Init_byebug_breakpoint(mByebug);
891
+
892
+ rb_global_variable(&breakpoints);
893
+ rb_global_variable(&catchpoints);
894
+ rb_global_variable(&tracepoints);
895
+ rb_global_variable(&raised_exception);
896
+ rb_global_variable(&threads);
897
+
898
+ idPuts = rb_intern("puts");
899
+ idEmpty = rb_intern("empty?");
900
+ }