needy_debugger 1.4.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 (187) hide show
  1. data/.gitignore +14 -0
  2. data/.travis.yml +8 -0
  3. data/AUTHORS +10 -0
  4. data/CHANGELOG.md +68 -0
  5. data/CONTRIBUTING.md +1 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +23 -0
  8. data/OLDER_CHANGELOG +334 -0
  9. data/OLD_CHANGELOG +5655 -0
  10. data/OLD_README +122 -0
  11. data/README.md +141 -0
  12. data/Rakefile +78 -0
  13. data/bin/rdebug +397 -0
  14. data/doc/.cvsignore +42 -0
  15. data/doc/Makefile.am +63 -0
  16. data/doc/emacs-notes.txt +38 -0
  17. data/doc/hanoi.rb +35 -0
  18. data/doc/primes.rb +28 -0
  19. data/doc/rdebug-emacs.texi +1030 -0
  20. data/doc/ruby-debug.texi +3791 -0
  21. data/doc/test-tri2.rb +18 -0
  22. data/doc/tri3.rb +8 -0
  23. data/doc/triangle.rb +12 -0
  24. data/emacs/Makefile.am +130 -0
  25. data/emacs/rdebug-annotate.el +385 -0
  26. data/emacs/rdebug-breaks.el +407 -0
  27. data/emacs/rdebug-cmd.el +92 -0
  28. data/emacs/rdebug-core.el +502 -0
  29. data/emacs/rdebug-dbg.el +62 -0
  30. data/emacs/rdebug-error.el +79 -0
  31. data/emacs/rdebug-fns.el +111 -0
  32. data/emacs/rdebug-frames.el +230 -0
  33. data/emacs/rdebug-gud.el +242 -0
  34. data/emacs/rdebug-help.el +104 -0
  35. data/emacs/rdebug-info.el +83 -0
  36. data/emacs/rdebug-layouts.el +180 -0
  37. data/emacs/rdebug-locring.el +118 -0
  38. data/emacs/rdebug-output.el +106 -0
  39. data/emacs/rdebug-regexp.el +118 -0
  40. data/emacs/rdebug-secondary.el +260 -0
  41. data/emacs/rdebug-shortkey.el +175 -0
  42. data/emacs/rdebug-source.el +568 -0
  43. data/emacs/rdebug-track.el +392 -0
  44. data/emacs/rdebug-varbuf.el +150 -0
  45. data/emacs/rdebug-vars.el +125 -0
  46. data/emacs/rdebug-watch.el +132 -0
  47. data/emacs/rdebug.el +326 -0
  48. data/emacs/test/elk-test.el +242 -0
  49. data/emacs/test/test-annotate.el +103 -0
  50. data/emacs/test/test-cmd.el +116 -0
  51. data/emacs/test/test-core.el +104 -0
  52. data/emacs/test/test-fns.el +65 -0
  53. data/emacs/test/test-frames.el +62 -0
  54. data/emacs/test/test-gud.el +35 -0
  55. data/emacs/test/test-indent.el +58 -0
  56. data/emacs/test/test-regexp.el +144 -0
  57. data/emacs/test/test-shortkey.el +61 -0
  58. data/ext/ruby_debug/192/breakpoint.c +586 -0
  59. data/ext/ruby_debug/192/ruby_debug.c +2645 -0
  60. data/ext/ruby_debug/192/ruby_debug.h +148 -0
  61. data/ext/ruby_debug/193/breakpoint.c +586 -0
  62. data/ext/ruby_debug/193/ruby_debug.c +2626 -0
  63. data/ext/ruby_debug/193/ruby_debug.h +148 -0
  64. data/ext/ruby_debug/200/breakpoint.c +586 -0
  65. data/ext/ruby_debug/200/ruby_debug.c +2692 -0
  66. data/ext/ruby_debug/200/ruby_debug.h +148 -0
  67. data/ext/ruby_debug/extconf.rb +94 -0
  68. data/lib/debugger.rb +5 -0
  69. data/lib/debugger/version.rb +5 -0
  70. data/lib/ruby-debug-base.rb +305 -0
  71. data/lib/ruby-debug.rb +177 -0
  72. data/lib/ruby-debug/command.rb +227 -0
  73. data/lib/ruby-debug/commands/breakpoints.rb +153 -0
  74. data/lib/ruby-debug/commands/catchpoint.rb +55 -0
  75. data/lib/ruby-debug/commands/condition.rb +49 -0
  76. data/lib/ruby-debug/commands/continue.rb +38 -0
  77. data/lib/ruby-debug/commands/control.rb +107 -0
  78. data/lib/ruby-debug/commands/display.rb +120 -0
  79. data/lib/ruby-debug/commands/edit.rb +48 -0
  80. data/lib/ruby-debug/commands/enable.rb +202 -0
  81. data/lib/ruby-debug/commands/eval.rb +176 -0
  82. data/lib/ruby-debug/commands/finish.rb +42 -0
  83. data/lib/ruby-debug/commands/frame.rb +301 -0
  84. data/lib/ruby-debug/commands/help.rb +56 -0
  85. data/lib/ruby-debug/commands/info.rb +467 -0
  86. data/lib/ruby-debug/commands/irb.rb +123 -0
  87. data/lib/ruby-debug/commands/jump.rb +66 -0
  88. data/lib/ruby-debug/commands/kill.rb +51 -0
  89. data/lib/ruby-debug/commands/list.rb +94 -0
  90. data/lib/ruby-debug/commands/method.rb +84 -0
  91. data/lib/ruby-debug/commands/quit.rb +50 -0
  92. data/lib/ruby-debug/commands/reload.rb +40 -0
  93. data/lib/ruby-debug/commands/save.rb +90 -0
  94. data/lib/ruby-debug/commands/set.rb +223 -0
  95. data/lib/ruby-debug/commands/show.rb +247 -0
  96. data/lib/ruby-debug/commands/skip.rb +35 -0
  97. data/lib/ruby-debug/commands/source.rb +36 -0
  98. data/lib/ruby-debug/commands/stepping.rb +81 -0
  99. data/lib/ruby-debug/commands/threads.rb +189 -0
  100. data/lib/ruby-debug/commands/tmate.rb +36 -0
  101. data/lib/ruby-debug/commands/trace.rb +57 -0
  102. data/lib/ruby-debug/commands/variables.rb +199 -0
  103. data/lib/ruby-debug/debugger.rb +5 -0
  104. data/lib/ruby-debug/helper.rb +69 -0
  105. data/lib/ruby-debug/interface.rb +232 -0
  106. data/lib/ruby-debug/processor.rb +474 -0
  107. data/man/rdebug.1 +241 -0
  108. data/needy_debugger.gemspec +31 -0
  109. data/old_scripts/Makefile.am +14 -0
  110. data/old_scripts/README.md +2 -0
  111. data/old_scripts/autogen.sh +4 -0
  112. data/old_scripts/configure.ac +12 -0
  113. data/old_scripts/rdbg.rb +33 -0
  114. data/old_scripts/runner.sh +7 -0
  115. data/old_scripts/svn2cl_usermap +3 -0
  116. data/test/.cvsignore +1 -0
  117. data/test/breakpoints_test.rb +365 -0
  118. data/test/conditions_test.rb +76 -0
  119. data/test/continue_test.rb +28 -0
  120. data/test/display_test.rb +141 -0
  121. data/test/edit_test.rb +55 -0
  122. data/test/eval_test.rb +92 -0
  123. data/test/examples/breakpoint1.rb +15 -0
  124. data/test/examples/breakpoint2.rb +7 -0
  125. data/test/examples/conditions.rb +4 -0
  126. data/test/examples/continue.rb +4 -0
  127. data/test/examples/display.rb +5 -0
  128. data/test/examples/edit.rb +3 -0
  129. data/test/examples/edit2.rb +3 -0
  130. data/test/examples/eval.rb +4 -0
  131. data/test/examples/finish.rb +20 -0
  132. data/test/examples/frame.rb +31 -0
  133. data/test/examples/help.rb +2 -0
  134. data/test/examples/info.rb +48 -0
  135. data/test/examples/info2.rb +3 -0
  136. data/test/examples/irb.rb +6 -0
  137. data/test/examples/jump.rb +14 -0
  138. data/test/examples/kill.rb +2 -0
  139. data/test/examples/list.rb +12 -0
  140. data/test/examples/method.rb +15 -0
  141. data/test/examples/post_mortem.rb +19 -0
  142. data/test/examples/quit.rb +2 -0
  143. data/test/examples/reload.rb +6 -0
  144. data/test/examples/restart.rb +6 -0
  145. data/test/examples/save.rb +3 -0
  146. data/test/examples/set.rb +3 -0
  147. data/test/examples/set_annotate.rb +12 -0
  148. data/test/examples/settings.rb +1 -0
  149. data/test/examples/show.rb +2 -0
  150. data/test/examples/source.rb +3 -0
  151. data/test/examples/stepping.rb +21 -0
  152. data/test/examples/thread.rb +32 -0
  153. data/test/examples/tmate.rb +10 -0
  154. data/test/examples/trace.rb +7 -0
  155. data/test/examples/trace_threads.rb +20 -0
  156. data/test/examples/variables.rb +26 -0
  157. data/test/finish_test.rb +48 -0
  158. data/test/frame_test.rb +140 -0
  159. data/test/help_test.rb +50 -0
  160. data/test/info_test.rb +325 -0
  161. data/test/irb_test.rb +81 -0
  162. data/test/jump_test.rb +70 -0
  163. data/test/kill_test.rb +47 -0
  164. data/test/list_test.rb +145 -0
  165. data/test/method_test.rb +70 -0
  166. data/test/post_mortem_test.rb +25 -0
  167. data/test/quit_test.rb +55 -0
  168. data/test/reload_test.rb +43 -0
  169. data/test/restart_test.rb +143 -0
  170. data/test/save_test.rb +92 -0
  171. data/test/set_test.rb +177 -0
  172. data/test/show_test.rb +292 -0
  173. data/test/source_test.rb +44 -0
  174. data/test/stepping_test.rb +118 -0
  175. data/test/support/breakpoint.rb +12 -0
  176. data/test/support/context.rb +14 -0
  177. data/test/support/matchers.rb +67 -0
  178. data/test/support/mocha_extensions.rb +71 -0
  179. data/test/support/processor.rb +7 -0
  180. data/test/support/test_dsl.rb +205 -0
  181. data/test/support/test_interface.rb +66 -0
  182. data/test/test_helper.rb +8 -0
  183. data/test/thread_test.rb +122 -0
  184. data/test/tmate_test.rb +43 -0
  185. data/test/trace_test.rb +154 -0
  186. data/test/variables_test.rb +114 -0
  187. metadata +352 -0
@@ -0,0 +1,2692 @@
1
+ #include <ruby.h>
2
+ #include <stdio.h>
3
+ #include <ctype.h>
4
+ #include <vm_core.h>
5
+ #include <iseq.h>
6
+ #include <version.h>
7
+ #include <eval_intern.h>
8
+ #include <insns.inc>
9
+ #include <insns_info.inc>
10
+ #include "ruby_debug.h"
11
+
12
+ #define DEBUG_VERSION "0.11"
13
+
14
+ #define FRAME_N(n) (&debug_context->frames[debug_context->stack_size-(n)-1])
15
+ #define GET_FRAME (FRAME_N(check_frame_number(debug_context, frame)))
16
+
17
+ #ifndef min
18
+ #define min(x,y) ((x) < (y) ? (x) : (y))
19
+ #endif
20
+
21
+ #define STACK_SIZE_INCREMENT 128
22
+
23
+ RUBY_EXTERN int rb_vm_get_sourceline(const rb_control_frame_t *cfp); /* from vm.c */
24
+
25
+ /* from iseq.c */
26
+ #ifdef HAVE_RB_ISEQ_COMPILE_ON_BASE
27
+ VALUE rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE absolute_path, VALUE line, rb_block_t *base_block, VALUE opt);
28
+ #elif RB_ISEQ_COMPILE_5ARGS
29
+ RUBY_EXTERN VALUE rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE filepath, VALUE line, VALUE opt);
30
+ #else
31
+ RUBY_EXTERN VALUE rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE line, VALUE opt);
32
+ #endif
33
+
34
+ typedef struct {
35
+ st_table *tbl;
36
+ } threads_table_t;
37
+
38
+ static VALUE tracing = Qfalse;
39
+ static VALUE locker = Qnil;
40
+ static VALUE post_mortem = Qfalse;
41
+ static VALUE keep_frame_binding = Qfalse;
42
+ static VALUE debug = Qfalse;
43
+ static VALUE track_frame_args = Qfalse;
44
+
45
+ static VALUE last_context = Qnil;
46
+ static VALUE last_thread = Qnil;
47
+ static debug_context_t *last_debug_context = NULL;
48
+
49
+ VALUE rdebug_threads_tbl = Qnil; /* Context for each of the threads */
50
+ VALUE mDebugger; /* Ruby Debugger Module object */
51
+
52
+ static VALUE opt_call_c_function;
53
+ static VALUE cThreadsTable;
54
+ static VALUE cContext;
55
+ static VALUE cDebugThread;
56
+
57
+ static VALUE rb_mObjectSpace;
58
+
59
+ static ID idAtBreakpoint;
60
+ static ID idAtCatchpoint;
61
+ static ID idAtLine;
62
+ static ID idAtReturn;
63
+ static ID idAtTracing;
64
+ static ID idList;
65
+
66
+ static int start_count = 0;
67
+ static int thnum_max = 0;
68
+ static int bkp_count = 0;
69
+ static int last_debugged_thnum = -1;
70
+ static unsigned long last_check = 0;
71
+ static unsigned long hook_count = 0;
72
+
73
+ static VALUE create_binding(VALUE);
74
+ static VALUE debug_stop(VALUE);
75
+ static void save_current_position(debug_context_t *);
76
+ static VALUE context_copy_args(debug_frame_t *);
77
+ static VALUE context_copy_locals(debug_context_t *,debug_frame_t *, VALUE);
78
+ static void context_suspend_0(debug_context_t *);
79
+ static void context_resume_0(debug_context_t *);
80
+ static void copy_scalar_args(debug_frame_t *);
81
+
82
+ typedef struct locked_thread_t {
83
+ VALUE thread_id;
84
+ struct locked_thread_t *next;
85
+ } locked_thread_t;
86
+
87
+ static locked_thread_t *locked_head = NULL;
88
+ static locked_thread_t *locked_tail = NULL;
89
+
90
+ /* "Step", "Next" and "Finish" do their work by saving information
91
+ about where to stop next. reset_stopping_points removes/resets this
92
+ information. */
93
+ inline static const char *
94
+ get_event_name(rb_event_flag_t _event)
95
+ {
96
+ switch (_event) {
97
+ case RUBY_EVENT_LINE:
98
+ return "line";
99
+ case RUBY_EVENT_CLASS:
100
+ return "class";
101
+ case RUBY_EVENT_END:
102
+ return "end";
103
+ case RUBY_EVENT_CALL:
104
+ return "call";
105
+ case RUBY_EVENT_RETURN:
106
+ return "return";
107
+ case RUBY_EVENT_C_CALL:
108
+ return "c-call";
109
+ case RUBY_EVENT_C_RETURN:
110
+ return "c-return";
111
+ case RUBY_EVENT_RAISE:
112
+ return "raise";
113
+ default:
114
+ return "unknown";
115
+ }
116
+ }
117
+
118
+ inline static void
119
+ reset_stepping_stop_points(debug_context_t *debug_context)
120
+ {
121
+ debug_context->dest_frame = -1;
122
+ debug_context->stop_line = -1;
123
+ debug_context->stop_next = -1;
124
+ }
125
+
126
+ inline static VALUE
127
+ real_class(VALUE klass)
128
+ {
129
+ if (klass) {
130
+ if (TYPE(klass) == T_ICLASS) {
131
+ return RBASIC(klass)->klass;
132
+ }
133
+ else if (FL_TEST(klass, FL_SINGLETON)) {
134
+ return rb_iv_get(klass, "__attached__");
135
+ }
136
+ }
137
+ return klass;
138
+ }
139
+
140
+ inline static VALUE
141
+ ref2id(VALUE obj)
142
+ {
143
+ return obj;
144
+ }
145
+
146
+ inline static VALUE
147
+ id2ref(VALUE id)
148
+ {
149
+ return id;
150
+ }
151
+
152
+ inline static VALUE
153
+ context_thread_0(debug_context_t *debug_context)
154
+ {
155
+ return id2ref(debug_context->thread_id);
156
+ }
157
+
158
+ static inline const rb_data_type_t *
159
+ threadptr_data_type(void)
160
+ {
161
+ static const rb_data_type_t *thread_data_type;
162
+ if (!thread_data_type)
163
+ {
164
+ VALUE current_thread = rb_thread_current();
165
+ thread_data_type = RTYPEDDATA_TYPE(current_thread);
166
+ }
167
+ return thread_data_type;
168
+ }
169
+
170
+ #define ruby_threadptr_data_type *threadptr_data_type()
171
+
172
+ // On ruby 1.9.3-xxx GET_THREAD is a macro, on ruby 2.0.0-p0 is a function. <ruby-ver>/vm_core.h
173
+ // CAREFUL: ruby_current_thread, GET_THREAD, rb_thread_set_current_raw
174
+ #if GET_THREAD
175
+ #define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
176
+ #define GET_THREAD2 GET_THREAD
177
+ #else
178
+ #warning "ruby 2.0.0-p0 GET_THREAD is a function."
179
+ rb_thread_t *ruby_current_thread;
180
+ rb_thread_t *GET_THREAD2(void)
181
+ {
182
+ ruby_current_thread = ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()));
183
+ return GET_THREAD();
184
+ }
185
+ #endif
186
+
187
+ static int
188
+ is_in_locked(VALUE thread_id)
189
+ {
190
+ locked_thread_t *node;
191
+
192
+ if(!locked_head)
193
+ return 0;
194
+
195
+ for(node = locked_head; node != locked_tail; node = node->next)
196
+ {
197
+ if(node->thread_id == thread_id) return 1;
198
+ }
199
+ return 0;
200
+ }
201
+
202
+ static void
203
+ add_to_locked(VALUE thread)
204
+ {
205
+ locked_thread_t *node;
206
+ VALUE thread_id = ref2id(thread);
207
+
208
+ if(is_in_locked(thread_id))
209
+ return;
210
+
211
+ node = ALLOC(locked_thread_t);
212
+ node->thread_id = thread_id;
213
+ node->next = NULL;
214
+ if(locked_tail)
215
+ locked_tail->next = node;
216
+ locked_tail = node;
217
+ if(!locked_head)
218
+ locked_head = node;
219
+ }
220
+
221
+ static VALUE
222
+ remove_from_locked(void)
223
+ {
224
+ VALUE thread;
225
+ locked_thread_t *node;
226
+
227
+ if(locked_head == NULL)
228
+ return Qnil;
229
+ node = locked_head;
230
+ locked_head = locked_head->next;
231
+ if(locked_tail == node)
232
+ locked_tail = NULL;
233
+ thread = id2ref(node->thread_id);
234
+ xfree(node);
235
+ return thread;
236
+ }
237
+
238
+ static int is_living_thread(VALUE thread);
239
+
240
+ static int
241
+ threads_table_mark_keyvalue(st_data_t key, st_data_t value, st_data_t tbl)
242
+ {
243
+ VALUE thread = id2ref((VALUE)key);
244
+ if (!value)
245
+ return ST_CONTINUE;
246
+
247
+ rb_gc_mark((VALUE)value);
248
+ if (is_living_thread(thread))
249
+ rb_gc_mark(thread);
250
+ else
251
+ st_insert((st_table *)tbl, key, 0);
252
+
253
+ return ST_CONTINUE;
254
+ }
255
+
256
+ static void
257
+ threads_table_mark(void* data)
258
+ {
259
+ threads_table_t *threads_table = (threads_table_t*)data;
260
+ st_table *tbl = threads_table->tbl;
261
+ st_foreach(tbl, threads_table_mark_keyvalue, (st_data_t)tbl);
262
+ }
263
+
264
+ static void
265
+ threads_table_free(void* data)
266
+ {
267
+ threads_table_t *threads_table = (threads_table_t*)data;
268
+ st_free_table(threads_table->tbl);
269
+ xfree(threads_table);
270
+ }
271
+
272
+ static VALUE
273
+ threads_table_create(void)
274
+ {
275
+ threads_table_t *threads_table;
276
+
277
+ threads_table = ALLOC(threads_table_t);
278
+ threads_table->tbl = st_init_numtable();
279
+ return Data_Wrap_Struct(cThreadsTable, threads_table_mark, threads_table_free, threads_table);
280
+ }
281
+
282
+ static void
283
+ threads_table_clear(VALUE table)
284
+ {
285
+ threads_table_t *threads_table;
286
+
287
+ Data_Get_Struct(table, threads_table_t, threads_table);
288
+ st_clear(threads_table->tbl);
289
+ }
290
+
291
+ static int
292
+ is_thread_alive(VALUE thread)
293
+ {
294
+ rb_thread_t *th;
295
+ GetThreadPtr(thread, th);
296
+ return th->status != THREAD_KILLED;
297
+ }
298
+
299
+ static int
300
+ is_living_thread(VALUE thread)
301
+ {
302
+ return rb_obj_is_kind_of(thread, rb_cThread) && is_thread_alive(thread);
303
+ }
304
+
305
+ static int
306
+ threads_table_check_i(st_data_t key, st_data_t value, st_data_t dummy)
307
+ {
308
+ VALUE thread;
309
+
310
+ if(!value)
311
+ {
312
+ return ST_DELETE;
313
+ }
314
+ thread = id2ref((VALUE)key);
315
+ if(!is_living_thread(thread))
316
+ {
317
+ return ST_DELETE;
318
+ }
319
+ return ST_CONTINUE;
320
+ }
321
+
322
+ static void
323
+ check_thread_contexts(void)
324
+ {
325
+ threads_table_t *threads_table;
326
+
327
+ Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
328
+ st_foreach(threads_table->tbl, threads_table_check_i, 0);
329
+ }
330
+
331
+ /*
332
+ * call-seq:
333
+ * Debugger.started? -> bool
334
+ *
335
+ * Returns +true+ the debugger is started.
336
+ */
337
+ static VALUE
338
+ debug_is_started(VALUE self)
339
+ {
340
+ return IS_STARTED ? Qtrue : Qfalse;
341
+ }
342
+
343
+ static void
344
+ debug_context_mark(void *data)
345
+ {
346
+ debug_frame_t *frame;
347
+ int i;
348
+
349
+ debug_context_t *debug_context = (debug_context_t *)data;
350
+ for(i = 0; i < debug_context->stack_size; i++)
351
+ {
352
+ frame = &(debug_context->frames[i]);
353
+ rb_gc_mark(frame->binding);
354
+ rb_gc_mark(frame->self);
355
+ rb_gc_mark(frame->arg_ary);
356
+ if(frame->dead)
357
+ {
358
+ rb_gc_mark(frame->info.copy.locals);
359
+ rb_gc_mark(frame->info.copy.args);
360
+ }
361
+ }
362
+ rb_gc_mark(debug_context->breakpoint);
363
+ }
364
+
365
+ static void
366
+ debug_context_free(void *data)
367
+ {
368
+ debug_context_t *debug_context = (debug_context_t *)data;
369
+ xfree(debug_context->frames);
370
+ }
371
+
372
+ static VALUE
373
+ debug_context_create(VALUE thread)
374
+ {
375
+ debug_context_t *debug_context;
376
+
377
+ debug_context = ALLOC(debug_context_t);
378
+ debug_context-> thnum = ++thnum_max;
379
+
380
+ debug_context->last_file = NULL;
381
+ debug_context->last_line = 0;
382
+ debug_context->flags = 0;
383
+
384
+ debug_context->stop_next = -1;
385
+ debug_context->dest_frame = -1;
386
+ debug_context->stop_line = -1;
387
+ debug_context->stop_frame = -1;
388
+ debug_context->stop_reason = CTX_STOP_NONE;
389
+ debug_context->stack_len = STACK_SIZE_INCREMENT;
390
+ debug_context->frames = ALLOC_N(debug_frame_t, STACK_SIZE_INCREMENT);
391
+ debug_context->stack_size = 0;
392
+ debug_context->thread_id = ref2id(thread);
393
+ debug_context->breakpoint = Qnil;
394
+ debug_context->jump_pc = NULL;
395
+ debug_context->jump_cfp = NULL;
396
+ debug_context->old_iseq_catch = NULL;
397
+ debug_context->thread_pause = 0;
398
+ if(rb_obj_class(thread) == cDebugThread)
399
+ CTX_FL_SET(debug_context, CTX_FL_IGNORE);
400
+ return Data_Wrap_Struct(cContext, debug_context_mark, debug_context_free, debug_context);
401
+ }
402
+
403
+ static VALUE
404
+ debug_context_dup(debug_context_t *debug_context, VALUE self)
405
+ {
406
+ debug_context_t *new_debug_context;
407
+ debug_frame_t *new_frame, *old_frame;
408
+ int i;
409
+
410
+ new_debug_context = ALLOC(debug_context_t);
411
+ memcpy(new_debug_context, debug_context, sizeof(debug_context_t));
412
+ new_debug_context->stop_next = -1;
413
+ new_debug_context->dest_frame = -1;
414
+ new_debug_context->stop_line = -1;
415
+ new_debug_context->stop_frame = -1;
416
+ new_debug_context->breakpoint = Qnil;
417
+ CTX_FL_SET(new_debug_context, CTX_FL_DEAD);
418
+ new_debug_context->frames = ALLOC_N(debug_frame_t, debug_context->stack_size);
419
+ new_debug_context->stack_len = debug_context->stack_size;
420
+ memcpy(new_debug_context->frames, debug_context->frames, sizeof(debug_frame_t) * debug_context->stack_size);
421
+ for(i = 0; i < debug_context->stack_size; i++)
422
+ {
423
+ new_frame = &(new_debug_context->frames[i]);
424
+ old_frame = &(debug_context->frames[i]);
425
+ new_frame->dead = 1;
426
+ new_frame->info.copy.args = context_copy_args(old_frame);
427
+ new_frame->info.copy.locals = context_copy_locals(debug_context, old_frame, self);
428
+ }
429
+ return Data_Wrap_Struct(cContext, debug_context_mark, debug_context_free, new_debug_context);
430
+ }
431
+
432
+ static void
433
+ thread_context_lookup(VALUE thread, VALUE *context, debug_context_t **debug_context, int create)
434
+ {
435
+ threads_table_t *threads_table;
436
+ VALUE thread_id;
437
+ debug_context_t *l_debug_context;
438
+
439
+ debug_check_started();
440
+
441
+ if(last_thread == thread && last_context != Qnil)
442
+ {
443
+ *context = last_context;
444
+ if(debug_context)
445
+ *debug_context = last_debug_context;
446
+ return;
447
+ }
448
+ thread_id = ref2id(thread);
449
+ Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
450
+ if(!st_lookup(threads_table->tbl, thread_id, context) || !*context)
451
+ {
452
+ if (create)
453
+ {
454
+ *context = debug_context_create(thread);
455
+ st_insert(threads_table->tbl, thread_id, *context);
456
+ }
457
+ else
458
+ {
459
+ *context = 0;
460
+ if (debug_context)
461
+ *debug_context = NULL;
462
+ return;
463
+ }
464
+ }
465
+
466
+ Data_Get_Struct(*context, debug_context_t, l_debug_context);
467
+ if(debug_context)
468
+ *debug_context = l_debug_context;
469
+
470
+ last_thread = thread;
471
+ last_context = *context;
472
+ last_debug_context = l_debug_context;
473
+ }
474
+
475
+ static VALUE
476
+ call_at_line_unprotected(VALUE args)
477
+ {
478
+ VALUE context;
479
+ context = *RARRAY_PTR(args);
480
+ return rb_funcall2(context, idAtLine, RARRAY_LEN(args) - 1, RARRAY_PTR(args) + 1);
481
+ }
482
+
483
+ static VALUE
484
+ call_at_line(VALUE context, debug_context_t *debug_context, VALUE file, VALUE line)
485
+ {
486
+ VALUE args;
487
+
488
+ last_debugged_thnum = debug_context->thnum;
489
+ save_current_position(debug_context);
490
+
491
+ args = rb_ary_new3(3, context, file, line);
492
+ return rb_protect(call_at_line_unprotected, args, 0);
493
+ }
494
+
495
+ static void
496
+ save_call_frame(rb_event_flag_t _event, debug_context_t *debug_context, VALUE self, char *file, int line, ID mid)
497
+ {
498
+ VALUE binding;
499
+ debug_frame_t *debug_frame;
500
+ int frame_n;
501
+
502
+ binding = self && RTEST(keep_frame_binding)? create_binding(self) : Qnil;
503
+
504
+ frame_n = debug_context->stack_size++;
505
+ if(frame_n >= debug_context->stack_len)
506
+ {
507
+ debug_context->stack_len += STACK_SIZE_INCREMENT;
508
+ debug_context->frames = REALLOC_N(debug_context->frames, debug_frame_t, debug_context->stack_len);
509
+ }
510
+ debug_frame = &debug_context->frames[frame_n];
511
+ debug_frame->file = file;
512
+ debug_frame->line = line;
513
+ debug_frame->binding = binding;
514
+ debug_frame->id = mid;
515
+ debug_frame->orig_id = mid;
516
+ debug_frame->dead = 0;
517
+ debug_frame->self = self;
518
+ debug_frame->arg_ary = Qnil;
519
+ debug_frame->argc = GET_THREAD2()->cfp->iseq->argc;
520
+ debug_frame->info.runtime.cfp = GET_THREAD2()->cfp;
521
+ #if VM_DEBUG_BP_CHECK
522
+ debug_frame->info.runtime.bp = GET_THREAD2()->cfp->bp_check;
523
+ #else
524
+ debug_frame->info.runtime.bp = GET_THREAD2()->cfp->bp;
525
+ #endif
526
+ debug_frame->info.runtime.block_iseq = GET_THREAD2()->cfp->block_iseq;
527
+ debug_frame->info.runtime.block_pc = NULL;
528
+ debug_frame->info.runtime.last_pc = GET_THREAD2()->cfp->pc;
529
+ if (RTEST(track_frame_args))
530
+ copy_scalar_args(debug_frame);
531
+ }
532
+
533
+
534
+ #if defined DOSISH
535
+ #define isdirsep(x) ((x) == '/' || (x) == '\\')
536
+ #else
537
+ #define isdirsep(x) ((x) == '/')
538
+ #endif
539
+
540
+ int
541
+ filename_cmp(VALUE source, char *file)
542
+ {
543
+ char *source_ptr, *file_ptr;
544
+ int s_len, f_len, min_len;
545
+ int s,f;
546
+ int dirsep_flag = 0;
547
+
548
+ s_len = RSTRING_LEN(source);
549
+ f_len = strlen(file);
550
+ min_len = min(s_len, f_len);
551
+
552
+ source_ptr = RSTRING_PTR(source);
553
+ file_ptr = file;
554
+
555
+ for( s = s_len - 1, f = f_len - 1; s >= s_len - min_len && f >= f_len - min_len; s--, f-- )
556
+ {
557
+ if((source_ptr[s] == '.' || file_ptr[f] == '.') && dirsep_flag)
558
+ return 1;
559
+ if(isdirsep(source_ptr[s]) && isdirsep(file_ptr[f]))
560
+ dirsep_flag = 1;
561
+ #ifdef DOSISH_DRIVE_LETTER
562
+ else if (s == 0)
563
+ return(toupper(source_ptr[s]) == toupper(file_ptr[f]));
564
+ #endif
565
+ else if(source_ptr[s] != file_ptr[f])
566
+ return 0;
567
+ }
568
+ return 1;
569
+ }
570
+
571
+ static VALUE
572
+ create_binding(VALUE self)
573
+ {
574
+ return(rb_binding_new());
575
+ }
576
+
577
+ inline static debug_frame_t *
578
+ get_top_frame(debug_context_t *debug_context)
579
+ {
580
+ if(debug_context->stack_size == 0)
581
+ return NULL;
582
+ else
583
+ return &(debug_context->frames[debug_context->stack_size-1]);
584
+ }
585
+
586
+ inline static void
587
+ save_top_binding(debug_context_t *debug_context, VALUE binding)
588
+ {
589
+ debug_frame_t *debug_frame;
590
+ debug_frame = get_top_frame(debug_context);
591
+ if(debug_frame)
592
+ debug_frame->binding = binding;
593
+ }
594
+
595
+ inline static void
596
+ set_frame_source(rb_event_flag_t event, debug_context_t *debug_context, VALUE self, char *file, int line, ID mid)
597
+ {
598
+ debug_frame_t *top_frame;
599
+ top_frame = get_top_frame(debug_context);
600
+ if(top_frame)
601
+ {
602
+ if (top_frame->info.runtime.block_iseq == GET_THREAD2()->cfp->iseq)
603
+ {
604
+ top_frame->info.runtime.block_pc = GET_THREAD2()->cfp->pc;
605
+ top_frame->binding = create_binding(self); /* block entered; need to rebind */
606
+ }
607
+ else if ((top_frame->info.runtime.block_pc != NULL) && (GET_THREAD2()->cfp->pc == top_frame->info.runtime.block_pc))
608
+ {
609
+ top_frame->binding = create_binding(self); /* block re-entered; need to rebind */
610
+ }
611
+
612
+ top_frame->info.runtime.block_iseq = GET_THREAD2()->cfp->block_iseq;
613
+ if (event == RUBY_EVENT_LINE)
614
+ top_frame->info.runtime.last_pc = GET_THREAD2()->cfp->pc;
615
+ top_frame->self = self;
616
+ top_frame->file = file;
617
+ top_frame->line = line;
618
+ top_frame->id = mid;
619
+ }
620
+ }
621
+
622
+ inline static void
623
+ reset_frame_mid(debug_context_t *debug_context)
624
+ {
625
+ debug_frame_t *top_frame;
626
+ top_frame = get_top_frame(debug_context);
627
+ if(top_frame)
628
+ {
629
+ top_frame->id = 0;
630
+ }
631
+ }
632
+
633
+ static void
634
+ save_current_position(debug_context_t *debug_context)
635
+ {
636
+ debug_frame_t *debug_frame;
637
+
638
+ debug_frame = get_top_frame(debug_context);
639
+ if(!debug_frame) return;
640
+ debug_context->last_file = debug_frame->file;
641
+ debug_context->last_line = debug_frame->line;
642
+ CTX_FL_UNSET(debug_context, CTX_FL_ENABLE_BKPT);
643
+ CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
644
+ CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
645
+ }
646
+
647
+ inline static int
648
+ c_call_new_frame_p(VALUE klass, ID mid)
649
+ {
650
+ klass = real_class(klass);
651
+ if(rb_block_given_p()) return 1;
652
+ if(klass == rb_cProc || klass == rb_mKernel || klass == rb_cModule) return 1;
653
+ return 0;
654
+ }
655
+
656
+ static void
657
+ call_at_line_check(VALUE self, debug_context_t *debug_context, VALUE breakpoint, VALUE context, char *file, int line)
658
+ {
659
+ VALUE binding = self? create_binding(self) : Qnil;
660
+ save_top_binding(debug_context, binding);
661
+
662
+ debug_context->stop_reason = CTX_STOP_STEP;
663
+
664
+ /* check breakpoint expression */
665
+ if(breakpoint != Qnil)
666
+ {
667
+ if(!check_breakpoint_expression(breakpoint, binding))
668
+ return;// TODO
669
+ if(!check_breakpoint_hit_condition(breakpoint))
670
+ return;// TODO
671
+ if(breakpoint != debug_context->breakpoint)
672
+ {
673
+ debug_context->stop_reason = CTX_STOP_BREAKPOINT;
674
+ rb_funcall(context, idAtBreakpoint, 1, breakpoint);
675
+ }
676
+ else
677
+ debug_context->breakpoint = Qnil;
678
+ }
679
+
680
+ reset_stepping_stop_points(debug_context);
681
+ call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line));
682
+ }
683
+
684
+ static struct iseq_catch_table_entry *
685
+ create_catch_table(debug_context_t *debug_context, unsigned long cont)
686
+ {
687
+ struct iseq_catch_table_entry *catch_table = &debug_context->catch_table.tmp_catch_table;
688
+
689
+ GET_THREAD2()->parse_in_eval++;
690
+ GET_THREAD2()->mild_compile_error++;
691
+ /* compiling with option Qfalse (no options) prevents debug hook calls during this catch routine */
692
+ #ifdef HAVE_RB_ISEQ_COMPILE_ON_BASE
693
+ catch_table->iseq = rb_iseq_compile_with_option(
694
+ rb_str_new_cstr(""), rb_str_new_cstr("(exception catcher)"), Qnil, INT2FIX(1), NULL, Qfalse);
695
+ #elif RB_ISEQ_COMPILE_5ARGS
696
+ catch_table->iseq = rb_iseq_compile_with_option(
697
+ rb_str_new_cstr(""), rb_str_new_cstr("(exception catcher)"), Qnil, INT2FIX(1), Qfalse);
698
+ #else
699
+ catch_table->iseq = rb_iseq_compile_with_option(
700
+ rb_str_new_cstr(""), rb_str_new_cstr("(exception catcher)"), INT2FIX(1), Qfalse);
701
+ #endif
702
+ GET_THREAD2()->mild_compile_error--;
703
+ GET_THREAD2()->parse_in_eval--;
704
+
705
+ catch_table->type = CATCH_TYPE_RESCUE;
706
+ catch_table->start = 0;
707
+ catch_table->end = ULONG_MAX;
708
+ catch_table->cont = cont;
709
+ catch_table->sp = 0;
710
+
711
+ return(catch_table);
712
+ }
713
+
714
+ #ifdef RUBY_EVENT_VM
715
+ static int
716
+ set_thread_event_flag_i(st_data_t key, st_data_t val, st_data_t flag)
717
+ {
718
+ VALUE thval = (VALUE)key;
719
+ rb_thread_t *th;
720
+ GetThreadPtr(thval, th);
721
+ th->event_flags |= RUBY_EVENT_VM;
722
+
723
+ return(ST_CONTINUE);
724
+ }
725
+ #endif
726
+
727
+ static void
728
+ debug_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
729
+ {
730
+ VALUE context;
731
+ VALUE breakpoint = Qnil, binding = Qnil;
732
+ debug_context_t *debug_context;
733
+ char *file = (char*)rb_sourcefile();
734
+ int line = rb_sourceline();
735
+ int moved = 0;
736
+ rb_thread_t *thread = GET_THREAD2();
737
+ struct rb_iseq_struct *iseq = thread->cfp->iseq;
738
+
739
+ hook_count++;
740
+
741
+ if (((iseq == NULL) || (file == NULL)) && (event != RUBY_EVENT_RAISE))
742
+ return;
743
+ thread_context_lookup(thread->self, &context, &debug_context, 1);
744
+
745
+ if ((event == RUBY_EVENT_LINE) || (event == RUBY_EVENT_CALL))
746
+ {
747
+ mid = iseq->defined_method_id;
748
+ klass = iseq->klass;
749
+ }
750
+
751
+ #ifdef ID_ALLOCATOR
752
+ if (mid == ID_ALLOCATOR) return;
753
+ #endif
754
+
755
+ /* return if thread is marked as 'ignored'.
756
+ debugger's threads are marked this way
757
+ */
758
+ if(CTX_FL_TEST(debug_context, CTX_FL_IGNORE)) return;
759
+
760
+ while(1)
761
+ {
762
+ /* halt execution of the current thread if the debugger
763
+ is activated in another
764
+ */
765
+ while(locker != Qnil && locker != thread->self)
766
+ {
767
+ add_to_locked(thread->self);
768
+ rb_thread_stop();
769
+ }
770
+
771
+ /* stop the current thread if it's marked as suspended */
772
+ if(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND) && locker != thread->self)
773
+ {
774
+ CTX_FL_SET(debug_context, CTX_FL_WAS_RUNNING);
775
+ rb_thread_stop();
776
+ }
777
+ else break;
778
+ }
779
+
780
+ /* return if the current thread is the locker */
781
+ if (locker != Qnil) return;
782
+
783
+ /* only the current thread can proceed */
784
+ locker = thread->self;
785
+
786
+ /* restore catch tables removed for jump */
787
+ if (debug_context->old_iseq_catch != NULL)
788
+ {
789
+ int i = 0;
790
+ while (debug_context->old_iseq_catch[i].iseq != NULL)
791
+ {
792
+ debug_context->old_iseq_catch[i].iseq->catch_table = debug_context->old_iseq_catch[i].catch_table;
793
+ debug_context->old_iseq_catch[i].iseq->catch_table_size = debug_context->old_iseq_catch[i].catch_table_size;
794
+ i++;
795
+ }
796
+ free(debug_context->old_iseq_catch);
797
+ debug_context->old_iseq_catch = NULL;
798
+ }
799
+
800
+ #ifdef RUBY_EVENT_VM
801
+ /* make sure all threads have event flag set so we'll get its events */
802
+ st_foreach(thread->vm->living_threads, set_thread_event_flag_i, 0);
803
+ #endif
804
+
805
+ /* remove any frames that are now out of scope */
806
+ while(debug_context->stack_size > 0)
807
+ {
808
+ #if VM_DEBUG_BP_CHECK
809
+ if (debug_context->frames[debug_context->stack_size - 1].info.runtime.bp <= thread->cfp->bp_check)
810
+ #else
811
+ if (debug_context->frames[debug_context->stack_size - 1].info.runtime.bp <= thread->cfp->bp)
812
+ #endif
813
+ break;
814
+ debug_context->stack_size--;
815
+ }
816
+
817
+ if (debug_context->thread_pause)
818
+ {
819
+ debug_context->thread_pause = 0;
820
+ debug_context->stop_next = 1;
821
+ debug_context->dest_frame = -1;
822
+ moved = 1;
823
+ }
824
+ else
825
+ {
826
+ /* ignore a skipped section of code */
827
+ if (CTX_FL_TEST(debug_context, CTX_FL_SKIPPED))
828
+ goto cleanup;
829
+
830
+ if ((event == RUBY_EVENT_LINE) && (debug_context->stack_size > 0) &&
831
+ (get_top_frame(debug_context)->line == line) && (get_top_frame(debug_context)->info.runtime.cfp->iseq == iseq) &&
832
+ !CTX_FL_TEST(debug_context, CTX_FL_CATCHING))
833
+ {
834
+ /* Sometimes duplicate RUBY_EVENT_LINE messages get generated by the compiler.
835
+ * Ignore them. */
836
+ goto cleanup;
837
+ }
838
+ }
839
+
840
+ if(debug == Qtrue)
841
+ fprintf(stderr, "%s:%d [%s] %s\n", file, line, get_event_name(event), rb_id2name(mid));
842
+
843
+ /* There can be many event calls per line, but we only want
844
+ *one* breakpoint per line. */
845
+ if(debug_context->last_line != line || debug_context->last_file == NULL ||
846
+ strcmp(debug_context->last_file, file) != 0)
847
+ {
848
+ CTX_FL_SET(debug_context, CTX_FL_ENABLE_BKPT);
849
+ moved = 1;
850
+ }
851
+
852
+ if(event != RUBY_EVENT_LINE)
853
+ CTX_FL_SET(debug_context, CTX_FL_STEPPED);
854
+
855
+ switch(event)
856
+ {
857
+ case RUBY_EVENT_LINE:
858
+ {
859
+ if(debug_context->stack_size == 0)
860
+ save_call_frame(event, debug_context, self, file, line, mid);
861
+ else
862
+ set_frame_source(event, debug_context, self, file, line, mid);
863
+
864
+ if (CTX_FL_TEST(debug_context, CTX_FL_CATCHING))
865
+ {
866
+ debug_frame_t *top_frame = get_top_frame(debug_context);
867
+
868
+ if (top_frame != NULL)
869
+ {
870
+ rb_control_frame_t *cfp = top_frame->info.runtime.cfp;
871
+ int hit_count;
872
+
873
+ /* restore the proper catch table */
874
+ cfp->iseq->catch_table_size = debug_context->catch_table.old_catch_table_size;
875
+ cfp->iseq->catch_table = debug_context->catch_table.old_catch_table;
876
+
877
+ /* send catchpoint notification */
878
+ hit_count = INT2FIX(FIX2INT(rb_hash_aref(rdebug_catchpoints,
879
+ debug_context->catch_table.mod_name)+1));
880
+ rb_hash_aset(rdebug_catchpoints, debug_context->catch_table.mod_name, hit_count);
881
+ debug_context->stop_reason = CTX_STOP_CATCHPOINT;
882
+ rb_funcall(context, idAtCatchpoint, 1, debug_context->catch_table.errinfo);
883
+ if(self && binding == Qnil)
884
+ binding = create_binding(self);
885
+ save_top_binding(debug_context, binding);
886
+ call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line));
887
+ }
888
+
889
+ /* now allow the next exception to be caught */
890
+ CTX_FL_UNSET(debug_context, CTX_FL_CATCHING);
891
+ break;
892
+ }
893
+
894
+ if(RTEST(tracing) || CTX_FL_TEST(debug_context, CTX_FL_TRACING))
895
+ rb_funcall(context, idAtTracing, 2, rb_str_new2(file), INT2FIX(line));
896
+
897
+ if(debug_context->dest_frame == -1 ||
898
+ debug_context->stack_size == debug_context->dest_frame)
899
+ {
900
+ if(moved || !CTX_FL_TEST(debug_context, CTX_FL_FORCE_MOVE))
901
+ debug_context->stop_next--;
902
+ if(debug_context->stop_next < 0)
903
+ debug_context->stop_next = -1;
904
+ if(moved || (CTX_FL_TEST(debug_context, CTX_FL_STEPPED) &&
905
+ !CTX_FL_TEST(debug_context, CTX_FL_FORCE_MOVE)))
906
+ {
907
+ debug_context->stop_line--;
908
+ CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
909
+ }
910
+ }
911
+ else if(debug_context->stack_size < debug_context->dest_frame)
912
+ {
913
+ debug_context->stop_next = 0;
914
+ }
915
+
916
+ if(debug_context->stop_next == 0 || debug_context->stop_line == 0 ||
917
+ (breakpoint = check_breakpoints_by_pos(debug_context, file, line)) != Qnil)
918
+ {
919
+ call_at_line_check(self, debug_context, breakpoint, context, file, line);
920
+ }
921
+ break;
922
+ }
923
+ case RUBY_EVENT_CALL:
924
+ {
925
+ save_call_frame(event, debug_context, self, file, line, mid);
926
+ breakpoint = check_breakpoints_by_method(debug_context, klass, mid, self);
927
+ if(breakpoint != Qnil)
928
+ {
929
+ debug_frame_t *debug_frame;
930
+ debug_frame = get_top_frame(debug_context);
931
+ if(debug_frame)
932
+ binding = debug_frame->binding;
933
+ if(NIL_P(binding) && self)
934
+ binding = create_binding(self);
935
+ save_top_binding(debug_context, binding);
936
+
937
+ if(!check_breakpoint_expression(breakpoint, binding))
938
+ break;
939
+ if(!check_breakpoint_hit_condition(breakpoint))
940
+ break;
941
+ if(breakpoint != debug_context->breakpoint)
942
+ {
943
+ debug_context->stop_reason = CTX_STOP_BREAKPOINT;
944
+ rb_funcall(context, idAtBreakpoint, 1, breakpoint);
945
+ }
946
+ else
947
+ debug_context->breakpoint = Qnil;
948
+ call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line));
949
+ break;
950
+ }
951
+ breakpoint = check_breakpoints_by_pos(debug_context, file, line);
952
+ if (breakpoint != Qnil)
953
+ call_at_line_check(self, debug_context, breakpoint, context, file, line);
954
+ break;
955
+ }
956
+ case RUBY_EVENT_C_CALL:
957
+ {
958
+ if(c_call_new_frame_p(klass, mid))
959
+ save_call_frame(event, debug_context, self, file, line, mid);
960
+ else
961
+ set_frame_source(event, debug_context, self, file, line, mid);
962
+ break;
963
+ }
964
+ case RUBY_EVENT_C_RETURN:
965
+ {
966
+ /* note if a block is given we fall through! */
967
+ if(!rb_method_boundp(klass, mid, 0) || !c_call_new_frame_p(klass, mid))
968
+ break;
969
+ }
970
+ case RUBY_EVENT_RETURN:
971
+ case RUBY_EVENT_END:
972
+ {
973
+ if(debug_context->stack_size == debug_context->stop_frame)
974
+ {
975
+ debug_context->stop_next = 1;
976
+ debug_context->stop_frame = 0;
977
+ /* NOTE: can't use call_at_line function here to trigger a debugger event.
978
+ this can lead to segfault. We should only unroll the stack on this event.
979
+ */
980
+ }
981
+ while(debug_context->stack_size > 0)
982
+ {
983
+ debug_context->stack_size--;
984
+ #if VM_DEBUG_BP_CHECK
985
+ if (debug_context->frames[debug_context->stack_size].info.runtime.bp <= GET_THREAD2()->cfp->bp_check)
986
+ #else
987
+ if (debug_context->frames[debug_context->stack_size].info.runtime.bp <= GET_THREAD2()->cfp->bp)
988
+ #endif
989
+ break;
990
+ }
991
+ CTX_FL_SET(debug_context, CTX_FL_ENABLE_BKPT);
992
+
993
+ break;
994
+ }
995
+ case RUBY_EVENT_CLASS:
996
+ {
997
+ reset_frame_mid(debug_context);
998
+ save_call_frame(event, debug_context, self, file, line, mid);
999
+ break;
1000
+ }
1001
+ case RUBY_EVENT_RAISE:
1002
+ {
1003
+ VALUE ancestors;
1004
+ VALUE expn_class, aclass;
1005
+ int i;
1006
+
1007
+ // set_frame_source(event, debug_context, self, file, line, mid);
1008
+
1009
+ if(post_mortem == Qtrue && self)
1010
+ {
1011
+ binding = create_binding(self);
1012
+ rb_ivar_set(rb_errinfo(), rb_intern("@__debug_file"), rb_str_new2(file));
1013
+ rb_ivar_set(rb_errinfo(), rb_intern("@__debug_line"), INT2FIX(line));
1014
+ rb_ivar_set(rb_errinfo(), rb_intern("@__debug_binding"), binding);
1015
+ rb_ivar_set(rb_errinfo(), rb_intern("@__debug_context"), debug_context_dup(debug_context, self));
1016
+ }
1017
+
1018
+ expn_class = rb_obj_class(rb_errinfo());
1019
+
1020
+ /* This code goes back to the earliest days of ruby-debug. It
1021
+ tends to disallow catching an exception via the
1022
+ "catchpoint" command. To address this one possiblilty is to
1023
+ move this after testing for catchponts. Kent however thinks
1024
+ there may be a misfeature in Ruby's eval.c: the problem was
1025
+ in the fact that Ruby doesn't reset exception flag on the
1026
+ current thread before it calls a notification handler.
1027
+
1028
+ See also the #ifdef'd code below as well.
1029
+ */
1030
+ #ifdef NORMAL_CODE
1031
+ if( !NIL_P(rb_class_inherited_p(expn_class, rb_eSystemExit)) )
1032
+ {
1033
+ debug_stop(mDebugger);
1034
+ break;
1035
+ }
1036
+ #endif
1037
+
1038
+ if (rdebug_catchpoints == Qnil ||
1039
+ (debug_context->stack_size == 0) ||
1040
+ CTX_FL_TEST(debug_context, CTX_FL_CATCHING) ||
1041
+ #ifdef _ST_NEW_
1042
+ st_get_num_entries(RHASH_TBL(rdebug_catchpoints)) == 0)
1043
+ #else
1044
+ (RHASH_TBL(rdebug_catchpoints)->num_entries) == 0)
1045
+ #endif
1046
+ break;
1047
+
1048
+ ancestors = rb_mod_ancestors(expn_class);
1049
+ for(i = 0; i < RARRAY_LEN(ancestors); i++)
1050
+ {
1051
+ VALUE mod_name;
1052
+ VALUE hit_count;
1053
+
1054
+ aclass = rb_ary_entry(ancestors, i);
1055
+ mod_name = rb_mod_name(aclass);
1056
+ hit_count = rb_hash_aref(rdebug_catchpoints, mod_name);
1057
+ if (hit_count != Qnil)
1058
+ {
1059
+ debug_frame_t *top_frame = get_top_frame(debug_context);
1060
+ rb_control_frame_t *cfp = top_frame->info.runtime.cfp;
1061
+
1062
+ /* save the current catch table */
1063
+ CTX_FL_SET(debug_context, CTX_FL_CATCHING);
1064
+ debug_context->catch_table.old_catch_table_size = cfp->iseq->catch_table_size;
1065
+ debug_context->catch_table.old_catch_table = cfp->iseq->catch_table;
1066
+ debug_context->catch_table.mod_name = mod_name;
1067
+ debug_context->catch_table.errinfo = rb_errinfo();
1068
+
1069
+ /* create a new catch table to catch this exception, and put it in the current iseq */
1070
+ cfp->iseq->catch_table_size = 1;
1071
+ cfp->iseq->catch_table =
1072
+ create_catch_table(debug_context, top_frame->info.runtime.last_pc - cfp->iseq->iseq_encoded - insn_len(BIN(trace)));
1073
+ break;
1074
+ }
1075
+ }
1076
+
1077
+ /* If we stop the debugger, we may not be able to trace into
1078
+ code that has an exception handler wrapped around it. So
1079
+ the alternative is to force the user to do his own
1080
+ Debugger.stop. */
1081
+ #ifdef NORMAL_CODE_MOVING_AFTER_
1082
+ if( !NIL_P(rb_class_inherited_p(expn_class, rb_eSystemExit)) )
1083
+ {
1084
+ debug_stop(mDebugger);
1085
+ break;
1086
+ }
1087
+ #endif
1088
+
1089
+ break;
1090
+ }
1091
+ }
1092
+
1093
+ cleanup:
1094
+
1095
+ debug_context->stop_reason = CTX_STOP_NONE;
1096
+
1097
+ /* check that all contexts point to alive threads */
1098
+ if(hook_count - last_check > 3000)
1099
+ {
1100
+ check_thread_contexts();
1101
+ last_check = hook_count;
1102
+ }
1103
+
1104
+ /* release a lock */
1105
+ locker = Qnil;
1106
+
1107
+ /* let the next thread to run */
1108
+ {
1109
+ VALUE next_thread = remove_from_locked();
1110
+ if(next_thread != Qnil)
1111
+ rb_thread_run(next_thread);
1112
+ }
1113
+ }
1114
+
1115
+ static VALUE
1116
+ debug_stop_i(VALUE self)
1117
+ {
1118
+ if(IS_STARTED)
1119
+ debug_stop(self);
1120
+ return Qnil;
1121
+ }
1122
+
1123
+ /*
1124
+ * call-seq:
1125
+ * Debugger.start_ -> bool
1126
+ * Debugger.start_ { ... } -> bool
1127
+ *
1128
+ * This method is internal and activates the debugger. Use
1129
+ * Debugger.start (from <tt>lib/ruby-debug-base.rb</tt>) instead.
1130
+ *
1131
+ * The return value is the value of !Debugger.started? <i>before</i>
1132
+ * issuing the +start+; That is, +true+ is returned, unless debugger
1133
+ * was previously started.
1134
+
1135
+ * If a block is given, it starts debugger and yields to block. When
1136
+ * the block is finished executing it stops the debugger with
1137
+ * Debugger.stop method. Inside the block you will probably want to
1138
+ * have a call to Debugger.debugger. For example:
1139
+ * Debugger.start{debugger; foo} # Stop inside of foo
1140
+ *
1141
+ * Also, ruby-debug only allows
1142
+ * one invocation of debugger at a time; nested Debugger.start's
1143
+ * have no effect and you can't use this inside the debugger itself.
1144
+ *
1145
+ * <i>Note that if you want to completely remove the debugger hook,
1146
+ * you must call Debugger.stop as many times as you called
1147
+ * Debugger.start method.</i>
1148
+ */
1149
+ static VALUE
1150
+ debug_start(VALUE self)
1151
+ {
1152
+ VALUE result;
1153
+ start_count++;
1154
+
1155
+ if(IS_STARTED)
1156
+ result = Qfalse;
1157
+ else
1158
+ {
1159
+ locker = Qnil;
1160
+ rdebug_breakpoints = rb_ary_new();
1161
+ rdebug_catchpoints = rb_hash_new();
1162
+ rdebug_threads_tbl = threads_table_create();
1163
+
1164
+ rb_add_event_hook(debug_event_hook, RUBY_EVENT_ALL, Qnil);
1165
+ result = Qtrue;
1166
+ }
1167
+
1168
+ if(rb_block_given_p())
1169
+ rb_ensure(rb_yield, self, debug_stop_i, self);
1170
+
1171
+ return result;
1172
+ }
1173
+
1174
+ /*
1175
+ * call-seq:
1176
+ * Debugger.stop -> bool
1177
+ *
1178
+ * This method disables the debugger. It returns +true+ if the debugger is disabled,
1179
+ * otherwise it returns +false+.
1180
+ *
1181
+ * <i>Note that if you want to complete remove the debugger hook,
1182
+ * you must call Debugger.stop as many times as you called
1183
+ * Debugger.start method.</i>
1184
+ */
1185
+ static VALUE
1186
+ debug_stop(VALUE self)
1187
+ {
1188
+ debug_check_started();
1189
+
1190
+ start_count--;
1191
+ if(start_count)
1192
+ return Qfalse;
1193
+
1194
+ rb_remove_event_hook(debug_event_hook);
1195
+
1196
+ locker = Qnil;
1197
+ rdebug_breakpoints = Qnil;
1198
+ rdebug_threads_tbl = Qnil;
1199
+
1200
+ return Qtrue;
1201
+ }
1202
+
1203
+ static int
1204
+ find_last_context_func(st_data_t key, st_data_t value, st_data_t *result_arg)
1205
+ {
1206
+ debug_context_t *debug_context;
1207
+ VALUE *result = (VALUE*)result_arg;
1208
+ if(!value)
1209
+ return ST_CONTINUE;
1210
+
1211
+ Data_Get_Struct((VALUE)value, debug_context_t, debug_context);
1212
+ if(debug_context->thnum == last_debugged_thnum)
1213
+ {
1214
+ *result = value;
1215
+ return ST_STOP;
1216
+ }
1217
+ return ST_CONTINUE;
1218
+ }
1219
+
1220
+ /*
1221
+ * call-seq:
1222
+ * Debugger.last_interrupted -> context
1223
+ *
1224
+ * Returns last debugged context.
1225
+ */
1226
+ static VALUE
1227
+ debug_last_interrupted(VALUE self)
1228
+ {
1229
+ VALUE result = Qnil;
1230
+ threads_table_t *threads_table;
1231
+
1232
+ debug_check_started();
1233
+
1234
+ Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
1235
+
1236
+ st_foreach(threads_table->tbl, find_last_context_func, (st_data_t)&result);
1237
+ return result;
1238
+ }
1239
+
1240
+ /*
1241
+ * call-seq:
1242
+ * Debugger.current_context -> context
1243
+ *
1244
+ * Returns current context.
1245
+ * <i>Note:</i> Debugger.current_context.thread == Thread.current
1246
+ */
1247
+ static VALUE
1248
+ debug_current_context(VALUE self)
1249
+ {
1250
+ VALUE thread, context;
1251
+
1252
+ debug_check_started();
1253
+
1254
+ thread = rb_thread_current();
1255
+ thread_context_lookup(thread, &context, NULL, 1);
1256
+
1257
+ return context;
1258
+ }
1259
+
1260
+ /*
1261
+ * call-seq:
1262
+ * Debugger.thread_context(thread) -> context
1263
+ *
1264
+ * Returns context of the thread passed as an argument.
1265
+ */
1266
+ static VALUE
1267
+ debug_thread_context(VALUE self, VALUE thread)
1268
+ {
1269
+ VALUE context;
1270
+
1271
+ debug_check_started();
1272
+ thread_context_lookup(thread, &context, NULL, 1);
1273
+ return context;
1274
+ }
1275
+
1276
+ /*
1277
+ * call-seq:
1278
+ * Debugger.contexts -> array
1279
+ *
1280
+ * Returns an array of all contexts.
1281
+ */
1282
+ static VALUE
1283
+ debug_contexts(VALUE self)
1284
+ {
1285
+ volatile VALUE list;
1286
+ volatile VALUE new_list;
1287
+ VALUE thread, context;
1288
+ threads_table_t *threads_table;
1289
+ debug_context_t *debug_context;
1290
+ int i;
1291
+
1292
+ debug_check_started();
1293
+
1294
+ new_list = rb_ary_new();
1295
+ list = rb_funcall(rb_cThread, idList, 0);
1296
+ for(i = 0; i < RARRAY_LEN(list); i++)
1297
+ {
1298
+ thread = rb_ary_entry(list, i);
1299
+ thread_context_lookup(thread, &context, NULL, 1);
1300
+ rb_ary_push(new_list, context);
1301
+ }
1302
+ threads_table_clear(rdebug_threads_tbl);
1303
+ Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
1304
+ for(i = 0; i < RARRAY_LEN(new_list); i++)
1305
+ {
1306
+ context = rb_ary_entry(new_list, i);
1307
+ Data_Get_Struct(context, debug_context_t, debug_context);
1308
+ st_insert(threads_table->tbl, debug_context->thread_id, context);
1309
+ }
1310
+
1311
+ return new_list;
1312
+ }
1313
+
1314
+ /*
1315
+ * call-seq:
1316
+ * Debugger.suspend -> Debugger
1317
+ *
1318
+ * Suspends all contexts.
1319
+ */
1320
+ static VALUE
1321
+ debug_suspend(VALUE self)
1322
+ {
1323
+ VALUE current, context;
1324
+ VALUE context_list;
1325
+ debug_context_t *debug_context;
1326
+ int i;
1327
+
1328
+ debug_check_started();
1329
+
1330
+ context_list = debug_contexts(self);
1331
+ thread_context_lookup(rb_thread_current(), &current, NULL, 1);
1332
+
1333
+ for(i = 0; i < RARRAY_LEN(context_list); i++)
1334
+ {
1335
+ context = rb_ary_entry(context_list, i);
1336
+ if(current == context)
1337
+ continue;
1338
+ Data_Get_Struct(context, debug_context_t, debug_context);
1339
+ context_suspend_0(debug_context);
1340
+ }
1341
+
1342
+ return self;
1343
+ }
1344
+
1345
+ /*
1346
+ * call-seq:
1347
+ * Debugger.resume -> Debugger
1348
+ *
1349
+ * Resumes all contexts.
1350
+ */
1351
+ static VALUE
1352
+ debug_resume(VALUE self)
1353
+ {
1354
+ VALUE current, context;
1355
+ VALUE context_list;
1356
+ debug_context_t *debug_context;
1357
+ int i;
1358
+
1359
+ debug_check_started();
1360
+
1361
+ context_list = debug_contexts(self);
1362
+
1363
+ thread_context_lookup(rb_thread_current(), &current, NULL, 1);
1364
+ for(i = 0; i < RARRAY_LEN(context_list); i++)
1365
+ {
1366
+ context = rb_ary_entry(context_list, i);
1367
+ if(current == context)
1368
+ continue;
1369
+ Data_Get_Struct(context, debug_context_t, debug_context);
1370
+ context_resume_0(debug_context);
1371
+ }
1372
+
1373
+ rb_thread_schedule();
1374
+
1375
+ return self;
1376
+ }
1377
+
1378
+ /*
1379
+ * call-seq:
1380
+ * Debugger.tracing -> bool
1381
+ *
1382
+ * Returns +true+ if the global tracing is activated.
1383
+ */
1384
+ static VALUE
1385
+ debug_tracing(VALUE self)
1386
+ {
1387
+ return tracing;
1388
+ }
1389
+
1390
+ /*
1391
+ * call-seq:
1392
+ * Debugger.tracing = bool
1393
+ *
1394
+ * Sets the global tracing flag.
1395
+ */
1396
+ static VALUE
1397
+ debug_set_tracing(VALUE self, VALUE value)
1398
+ {
1399
+ tracing = RTEST(value) ? Qtrue : Qfalse;
1400
+ return value;
1401
+ }
1402
+
1403
+ /*
1404
+ * call-seq:
1405
+ * Debugger.post_mortem? -> bool
1406
+ *
1407
+ * Returns +true+ if post-moterm debugging is enabled.
1408
+ */
1409
+ static VALUE
1410
+ debug_post_mortem(VALUE self)
1411
+ {
1412
+ return post_mortem;
1413
+ }
1414
+
1415
+ /*
1416
+ * call-seq:
1417
+ * Debugger.post_mortem = bool
1418
+ *
1419
+ * Sets post-moterm flag.
1420
+ * FOR INTERNAL USE ONLY.
1421
+ */
1422
+ static VALUE
1423
+ debug_set_post_mortem(VALUE self, VALUE value)
1424
+ {
1425
+ debug_check_started();
1426
+
1427
+ post_mortem = RTEST(value) ? Qtrue : Qfalse;
1428
+ return value;
1429
+ }
1430
+
1431
+ /*
1432
+ * call-seq:
1433
+ * Debugger.track_fame_args? -> bool
1434
+ *
1435
+ * Returns +true+ if the debugger track frame argument values on calls.
1436
+ */
1437
+ static VALUE
1438
+ debug_track_frame_args(VALUE self)
1439
+ {
1440
+ return track_frame_args;
1441
+ }
1442
+
1443
+ /*
1444
+ * call-seq:
1445
+ * Debugger.track_frame_args = bool
1446
+ *
1447
+ * Setting to +true+ will make the debugger save argument info on calls.
1448
+ */
1449
+ static VALUE
1450
+ debug_set_track_frame_args(VALUE self, VALUE value)
1451
+ {
1452
+ track_frame_args = RTEST(value) ? Qtrue : Qfalse;
1453
+ return value;
1454
+ }
1455
+
1456
+ /*
1457
+ * call-seq:
1458
+ * Debugger.keep_frame_binding? -> bool
1459
+ *
1460
+ * Returns +true+ if the debugger will collect frame bindings.
1461
+ */
1462
+ static VALUE
1463
+ debug_keep_frame_binding(VALUE self)
1464
+ {
1465
+ return keep_frame_binding;
1466
+ }
1467
+
1468
+ /*
1469
+ * call-seq:
1470
+ * Debugger.keep_frame_binding = bool
1471
+ *
1472
+ * Setting to +true+ will make the debugger create frame bindings.
1473
+ */
1474
+ static VALUE
1475
+ debug_set_keep_frame_binding(VALUE self, VALUE value)
1476
+ {
1477
+ keep_frame_binding = RTEST(value) ? Qtrue : Qfalse;
1478
+ return value;
1479
+ }
1480
+
1481
+ /* :nodoc: */
1482
+ static VALUE
1483
+ debug_debug(VALUE self)
1484
+ {
1485
+ return debug;
1486
+ }
1487
+
1488
+ /* :nodoc: */
1489
+ static VALUE
1490
+ debug_set_debug(VALUE self, VALUE value)
1491
+ {
1492
+ debug = RTEST(value) ? Qtrue : Qfalse;
1493
+ return value;
1494
+ }
1495
+
1496
+ /* :nodoc: */
1497
+ static VALUE
1498
+ debug_thread_inherited(VALUE klass)
1499
+ {
1500
+ rb_raise(rb_eRuntimeError, "Can't inherit Debugger::DebugThread class");
1501
+ }
1502
+
1503
+ /*
1504
+ * call-seq:
1505
+ * Debugger.debug_load(file, stop = false, increment_start = false) -> nil
1506
+ *
1507
+ * Same as Kernel#load but resets current context's frames.
1508
+ * +stop+ parameter forces the debugger to stop at the first line of code in the +file+
1509
+ * +increment_start+ determines if start_count should be incremented. When
1510
+ * control threads are used, they have to be set up before loading the
1511
+ * debugger; so here +increment_start+ will be false.
1512
+ * FOR INTERNAL USE ONLY.
1513
+ */
1514
+ static VALUE
1515
+ debug_debug_load(int argc, VALUE *argv, VALUE self)
1516
+ {
1517
+ VALUE file, stop, context, increment_start;
1518
+ debug_context_t *debug_context;
1519
+ int state = 0;
1520
+
1521
+ if(rb_scan_args(argc, argv, "12", &file, &stop, &increment_start) == 1)
1522
+ {
1523
+ stop = Qfalse;
1524
+ increment_start = Qtrue;
1525
+ }
1526
+
1527
+ debug_start(self);
1528
+ if (Qfalse == increment_start) start_count--;
1529
+
1530
+ context = debug_current_context(self);
1531
+ Data_Get_Struct(context, debug_context_t, debug_context);
1532
+ debug_context->stack_size = 0;
1533
+ if(RTEST(stop))
1534
+ debug_context->stop_next = 1;
1535
+ /* Initializing $0 to the script's path */
1536
+ ruby_script(RSTRING_PTR(file));
1537
+ rb_load_protect(file, 0, &state);
1538
+ if (0 != state)
1539
+ {
1540
+ VALUE errinfo = rb_errinfo();
1541
+ debug_suspend(self);
1542
+ reset_stepping_stop_points(debug_context);
1543
+ rb_set_errinfo(Qnil);
1544
+ return errinfo;
1545
+ }
1546
+
1547
+ /* We should run all at_exit handler's in order to provide,
1548
+ * for instance, a chance to run all defined test cases */
1549
+ rb_exec_end_proc();
1550
+
1551
+ /* We could have issued a Debugger.stop inside the debug
1552
+ session. */
1553
+ if (start_count > 0)
1554
+ debug_stop(self);
1555
+
1556
+ return Qnil;
1557
+ }
1558
+
1559
+ static VALUE
1560
+ set_current_skipped_status(VALUE status)
1561
+ {
1562
+ VALUE context;
1563
+ debug_context_t *debug_context;
1564
+
1565
+ context = debug_current_context(Qnil);
1566
+ Data_Get_Struct(context, debug_context_t, debug_context);
1567
+ if(status)
1568
+ CTX_FL_SET(debug_context, CTX_FL_SKIPPED);
1569
+ else
1570
+ CTX_FL_UNSET(debug_context, CTX_FL_SKIPPED);
1571
+ return Qnil;
1572
+ }
1573
+
1574
+ /*
1575
+ * call-seq:
1576
+ * Debugger.skip { block } -> obj or nil
1577
+ *
1578
+ * The code inside of the block is escaped from the debugger.
1579
+ */
1580
+ static VALUE
1581
+ debug_skip(VALUE self)
1582
+ {
1583
+ if (!rb_block_given_p()) {
1584
+ rb_raise(rb_eArgError, "called without a block");
1585
+ }
1586
+ if(!IS_STARTED)
1587
+ return rb_yield(Qnil);
1588
+ set_current_skipped_status(Qtrue);
1589
+ return rb_ensure(rb_yield, Qnil, set_current_skipped_status, Qfalse);
1590
+ }
1591
+
1592
+ static VALUE
1593
+ debug_at_exit_c(VALUE proc)
1594
+ {
1595
+ return rb_funcall(proc, rb_intern("call"), 0);
1596
+ }
1597
+
1598
+ static void
1599
+ debug_at_exit_i(VALUE proc)
1600
+ {
1601
+ if(!IS_STARTED)
1602
+ {
1603
+ debug_at_exit_c(proc);
1604
+ }
1605
+ else
1606
+ {
1607
+ set_current_skipped_status(Qtrue);
1608
+ rb_ensure(debug_at_exit_c, proc, set_current_skipped_status, Qfalse);
1609
+ }
1610
+ }
1611
+
1612
+ /*
1613
+ * call-seq:
1614
+ * Debugger.debug_at_exit { block } -> proc
1615
+ *
1616
+ * Register <tt>at_exit</tt> hook which is escaped from the debugger.
1617
+ * FOR INTERNAL USE ONLY.
1618
+ */
1619
+ static VALUE
1620
+ debug_at_exit(VALUE self)
1621
+ {
1622
+ VALUE proc;
1623
+ if (!rb_block_given_p())
1624
+ rb_raise(rb_eArgError, "called without a block");
1625
+ proc = rb_block_proc();
1626
+ rb_set_end_proc(debug_at_exit_i, proc);
1627
+ return proc;
1628
+ }
1629
+
1630
+ /*
1631
+ * call-seq:
1632
+ * context.step(steps, force = false)
1633
+ *
1634
+ * Stops the current context after a number of +steps+ are made.
1635
+ * +force+ parameter (if true) ensures that the cursor moves from the current line.
1636
+ */
1637
+ static VALUE
1638
+ context_stop_next(int argc, VALUE *argv, VALUE self)
1639
+ {
1640
+ VALUE steps, force;
1641
+ debug_context_t *debug_context;
1642
+
1643
+ debug_check_started();
1644
+
1645
+ rb_scan_args(argc, argv, "11", &steps, &force);
1646
+ if(FIX2INT(steps) < 0)
1647
+ rb_raise(rb_eRuntimeError, "Steps argument can't be negative.");
1648
+
1649
+ Data_Get_Struct(self, debug_context_t, debug_context);
1650
+ debug_context->stop_next = FIX2INT(steps);
1651
+ if(RTEST(force))
1652
+ CTX_FL_SET(debug_context, CTX_FL_FORCE_MOVE);
1653
+ else
1654
+ CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
1655
+
1656
+ return steps;
1657
+ }
1658
+
1659
+ /*
1660
+ * call-seq:
1661
+ * context.step_over(steps, frame = nil, force = false)
1662
+ *
1663
+ * Steps over a +steps+ number of times.
1664
+ * Make step over operation on +frame+, by default the current frame.
1665
+ * +force+ parameter (if true) ensures that the cursor moves from the current line.
1666
+ */
1667
+ static VALUE
1668
+ context_step_over(int argc, VALUE *argv, VALUE self)
1669
+ {
1670
+ VALUE lines, frame, force;
1671
+ debug_context_t *debug_context;
1672
+
1673
+ debug_check_started();
1674
+ Data_Get_Struct(self, debug_context_t, debug_context);
1675
+ if(debug_context->stack_size == 0)
1676
+ rb_raise(rb_eRuntimeError, "No frames collected.");
1677
+
1678
+ rb_scan_args(argc, argv, "12", &lines, &frame, &force);
1679
+ debug_context->stop_line = FIX2INT(lines);
1680
+ CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
1681
+ if(frame == Qnil)
1682
+ {
1683
+ debug_context->dest_frame = debug_context->stack_size;
1684
+ }
1685
+ else
1686
+ {
1687
+ if(FIX2INT(frame) < 0 && FIX2INT(frame) >= debug_context->stack_size)
1688
+ rb_raise(rb_eRuntimeError, "Destination frame is out of range.");
1689
+ debug_context->dest_frame = debug_context->stack_size - FIX2INT(frame);
1690
+ }
1691
+ if(RTEST(force))
1692
+ CTX_FL_SET(debug_context, CTX_FL_FORCE_MOVE);
1693
+ else
1694
+ CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
1695
+
1696
+ return Qnil;
1697
+ }
1698
+
1699
+ /*
1700
+ * call-seq:
1701
+ * context.stop_frame(frame)
1702
+ *
1703
+ * Stops when a frame with number +frame+ is activated. Implements +finish+ and +next+ commands.
1704
+ */
1705
+ static VALUE
1706
+ context_stop_frame(VALUE self, VALUE frame)
1707
+ {
1708
+ debug_context_t *debug_context;
1709
+
1710
+ debug_check_started();
1711
+ Data_Get_Struct(self, debug_context_t, debug_context);
1712
+ if(FIX2INT(frame) < 0 && FIX2INT(frame) >= debug_context->stack_size)
1713
+ rb_raise(rb_eRuntimeError, "Stop frame is out of range.");
1714
+ debug_context->stop_frame = debug_context->stack_size - FIX2INT(frame);
1715
+
1716
+ return frame;
1717
+ }
1718
+
1719
+ inline static int
1720
+ check_frame_number(debug_context_t *debug_context, VALUE frame)
1721
+ {
1722
+ int frame_n;
1723
+
1724
+ frame_n = FIX2INT(frame);
1725
+ if(frame_n < 0 || frame_n >= debug_context->stack_size)
1726
+ rb_raise(rb_eArgError, "Invalid frame number %d, stack (0...%d)",
1727
+ frame_n, debug_context->stack_size - 1);
1728
+ return frame_n;
1729
+ }
1730
+
1731
+ static int
1732
+ optional_frame_position(int argc, VALUE *argv) {
1733
+ unsigned int i_scanned;
1734
+ VALUE level;
1735
+
1736
+ if ((argc > 1) || (argc < 0))
1737
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 or 1)", argc);
1738
+ i_scanned = rb_scan_args(argc, argv, "01", &level);
1739
+ if (0 == i_scanned) {
1740
+ level = INT2FIX(0);
1741
+ }
1742
+ return level;
1743
+ }
1744
+
1745
+ /*
1746
+ * call-seq:
1747
+ * context.frame_args_info(frame_position=0) -> list
1748
+ if track_frame_args or nil otherwise
1749
+ *
1750
+ * Returns info saved about call arguments (if any saved).
1751
+ */
1752
+ static VALUE
1753
+ context_frame_args_info(int argc, VALUE *argv, VALUE self)
1754
+ {
1755
+ VALUE frame;
1756
+ debug_context_t *debug_context;
1757
+
1758
+ debug_check_started();
1759
+ frame = optional_frame_position(argc, argv);
1760
+ Data_Get_Struct(self, debug_context_t, debug_context);
1761
+
1762
+ return RTEST(track_frame_args) ? GET_FRAME->arg_ary : Qnil;
1763
+ }
1764
+
1765
+ /*
1766
+ * call-seq:
1767
+ * context.frame_binding(frame_position=0) -> binding
1768
+ *
1769
+ * Returns frame's binding.
1770
+ */
1771
+ static VALUE
1772
+ context_frame_binding(int argc, VALUE *argv, VALUE self)
1773
+ {
1774
+ VALUE frame;
1775
+ debug_context_t *debug_context;
1776
+
1777
+ debug_check_started();
1778
+ frame = optional_frame_position(argc, argv);
1779
+ Data_Get_Struct(self, debug_context_t, debug_context);
1780
+ return GET_FRAME->binding;
1781
+ }
1782
+
1783
+ /*
1784
+ * call-seq:
1785
+ * context.frame_method(frame_position=0) -> sym
1786
+ *
1787
+ * Returns the sym of the called method.
1788
+ */
1789
+ static VALUE
1790
+ context_frame_id(int argc, VALUE *argv, VALUE self)
1791
+ {
1792
+ ID id;
1793
+ VALUE frame;
1794
+ VALUE frame_id;
1795
+ debug_context_t *debug_context;
1796
+ rb_control_frame_t *cfp;
1797
+
1798
+ debug_check_started();
1799
+ frame = optional_frame_position(argc, argv);
1800
+ Data_Get_Struct(self, debug_context_t, debug_context);
1801
+
1802
+ cfp = GET_FRAME->info.runtime.cfp;
1803
+ #if defined HAVE_RB_CONTROL_FRAME_T_METHOD_ID
1804
+ frame_id = RUBYVM_CFUNC_FRAME_P(cfp) ? cfp->method_id : cfp->iseq->defined_method_id;
1805
+ #elif defined HAVE_RB_METHOD_ENTRY_T_CALLED_ID
1806
+ frame_id = RUBYVM_CFUNC_FRAME_P(cfp) ? cfp->me->called_id : cfp->iseq->defined_method_id;
1807
+ #endif
1808
+ return frame_id ? ID2SYM(frame_id) : Qnil;
1809
+ }
1810
+
1811
+ /*
1812
+ * call-seq:
1813
+ * context.frame_line(frame_position) -> int
1814
+ *
1815
+ * Returns the line number in the file.
1816
+ */
1817
+ static VALUE
1818
+ context_frame_line(int argc, VALUE *argv, VALUE self)
1819
+ {
1820
+ VALUE frame;
1821
+ debug_context_t *debug_context;
1822
+ rb_thread_t *th;
1823
+ rb_control_frame_t *cfp;
1824
+ VALUE *pc;
1825
+
1826
+ debug_check_started();
1827
+ frame = optional_frame_position(argc, argv);
1828
+ Data_Get_Struct(self, debug_context_t, debug_context);
1829
+ GetThreadPtr(context_thread_0(debug_context), th);
1830
+
1831
+ pc = GET_FRAME->info.runtime.last_pc;
1832
+ cfp = GET_FRAME->info.runtime.cfp;
1833
+ while (cfp >= th->cfp)
1834
+ {
1835
+ if ((cfp->iseq != NULL) && (pc >= cfp->iseq->iseq_encoded) && (pc < cfp->iseq->iseq_encoded + cfp->iseq->iseq_size))
1836
+ return(INT2FIX(rb_vm_get_sourceline(cfp)));
1837
+ cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp);
1838
+ }
1839
+
1840
+ return(INT2FIX(0));
1841
+ }
1842
+
1843
+ /*
1844
+ * call-seq:
1845
+ * context.frame_file(frame_position) -> string
1846
+ *
1847
+ * Returns the name of the file.
1848
+ */
1849
+ static VALUE
1850
+ context_frame_file(int argc, VALUE *argv, VALUE self)
1851
+ {
1852
+ VALUE frame;
1853
+ debug_context_t *debug_context;
1854
+
1855
+ debug_check_started();
1856
+ frame = optional_frame_position(argc, argv);
1857
+ Data_Get_Struct(self, debug_context_t, debug_context);
1858
+
1859
+ return rb_str_new_cstr(GET_FRAME->file);
1860
+ //return(GET_FRAME->info.runtime.cfp->iseq->filename);
1861
+ }
1862
+
1863
+ static int
1864
+ arg_value_is_small(VALUE val)
1865
+ {
1866
+ switch (TYPE(val)) {
1867
+ case T_FIXNUM: case T_FLOAT: case T_CLASS:
1868
+ case T_NIL: case T_MODULE: case T_FILE:
1869
+ case T_TRUE: case T_FALSE: case T_UNDEF:
1870
+ return 1;
1871
+ default:
1872
+ return SYMBOL_P(val);
1873
+ }
1874
+ }
1875
+
1876
+ /*
1877
+ * Save scalar arguments or a class name.
1878
+ */
1879
+ static void
1880
+ copy_scalar_args(debug_frame_t *debug_frame)
1881
+ {
1882
+ rb_control_frame_t *cfp;
1883
+ rb_iseq_t *iseq;
1884
+
1885
+ cfp = debug_frame->info.runtime.cfp;
1886
+ iseq = cfp->iseq;
1887
+
1888
+ if (iseq->local_table && iseq->argc)
1889
+ {
1890
+ int i;
1891
+ VALUE val;
1892
+
1893
+ debug_frame->arg_ary = rb_ary_new2(iseq->argc);
1894
+ for (i = 0; i < iseq->argc; i++)
1895
+ {
1896
+ if (!rb_is_local_id(iseq->local_table[i])) continue; /* skip flip states */
1897
+
1898
+ #ifdef HAVE_RB_CONTROL_FRAME_T_EP
1899
+ val = *(cfp->ep - iseq->local_size + i);
1900
+ #else
1901
+ val = *(cfp->dfp - iseq->local_size + i);
1902
+ #endif
1903
+
1904
+ if (arg_value_is_small(val))
1905
+ rb_ary_push(debug_frame->arg_ary, val);
1906
+ else
1907
+ rb_ary_push(debug_frame->arg_ary, rb_str_new2(rb_obj_classname(val)));
1908
+ }
1909
+ }
1910
+ }
1911
+
1912
+ /*
1913
+ * call-seq:
1914
+ * context.copy_args(frame) -> list of args
1915
+ *
1916
+ * Returns a array of argument names.
1917
+ */
1918
+ static VALUE
1919
+ context_copy_args(debug_frame_t *debug_frame)
1920
+ {
1921
+ rb_control_frame_t *cfp;
1922
+ rb_iseq_t *iseq;
1923
+
1924
+ cfp = debug_frame->info.runtime.cfp;
1925
+ iseq = cfp->iseq;
1926
+
1927
+ if (iseq->local_table && iseq->argc)
1928
+ {
1929
+ int i;
1930
+ VALUE list;
1931
+
1932
+ list = rb_ary_new2(iseq->argc);
1933
+ for (i = 0; i < iseq->argc; i++)
1934
+ {
1935
+ if (!rb_is_local_id(iseq->local_table[i])) continue; /* skip flip states */
1936
+ rb_ary_push(list, rb_id2str(iseq->local_table[i]));
1937
+ }
1938
+
1939
+ return(list);
1940
+ }
1941
+ return(rb_ary_new2(0));
1942
+ }
1943
+
1944
+ static VALUE
1945
+ context_copy_locals(debug_context_t *debug_context, debug_frame_t *debug_frame, VALUE self)
1946
+ {
1947
+ int i;
1948
+ rb_control_frame_t *cfp;
1949
+ rb_iseq_t *iseq;
1950
+ VALUE hash;
1951
+
1952
+ cfp = debug_frame->info.runtime.cfp;
1953
+ iseq = cfp->iseq;
1954
+ hash = rb_hash_new();
1955
+
1956
+ if (iseq->local_table != NULL)
1957
+ {
1958
+ /* Note rb_iseq_disasm() is instructive in coming up with this code */
1959
+ for (i = 0; i < iseq->local_table_size; i++)
1960
+ {
1961
+ VALUE str = rb_id2str(iseq->local_table[i]);
1962
+ if (str != 0)
1963
+ #ifdef HAVE_RB_CONTROL_FRAME_T_EP
1964
+ rb_hash_aset(hash, str, *(cfp->ep - iseq->local_size + i));
1965
+ #else
1966
+ rb_hash_aset(hash, str, *(cfp->dfp - iseq->local_size + i));
1967
+ #endif
1968
+ }
1969
+ }
1970
+
1971
+ iseq = cfp->block_iseq;
1972
+ if ((iseq != NULL) && (iseq->local_table != NULL) && (iseq != cfp->iseq))
1973
+ {
1974
+ rb_thread_t *th;
1975
+ rb_control_frame_t *block_frame = RUBY_VM_NEXT_CONTROL_FRAME(cfp);
1976
+ GetThreadPtr(context_thread_0(debug_context), th);
1977
+ while (block_frame > (rb_control_frame_t*)th->stack)
1978
+ {
1979
+ if (block_frame->iseq == cfp->block_iseq)
1980
+ {
1981
+ for (i = 0; i < iseq->local_table_size; i++)
1982
+ {
1983
+ VALUE str = rb_id2str(iseq->local_table[i]);
1984
+ if (str != 0)
1985
+ #ifdef HAVE_RB_CONTROL_FRAME_T_EP
1986
+ rb_hash_aset(hash, str, *(block_frame->ep - iseq->local_table_size + i - 1));
1987
+ #else
1988
+ rb_hash_aset(hash, str, *(block_frame->dfp - iseq->local_table_size + i - 1));
1989
+ #endif
1990
+ }
1991
+ return(hash);
1992
+ }
1993
+ block_frame = RUBY_VM_NEXT_CONTROL_FRAME(block_frame);
1994
+ }
1995
+ }
1996
+
1997
+ return(hash);
1998
+ }
1999
+
2000
+ /*
2001
+ * call-seq:
2002
+ * context.frame_locals(frame) -> hash
2003
+ *
2004
+ * Returns frame's local variables.
2005
+ */
2006
+ static VALUE
2007
+ context_frame_locals(int argc, VALUE *argv, VALUE self)
2008
+ {
2009
+ VALUE frame;
2010
+ debug_context_t *debug_context;
2011
+ debug_frame_t *debug_frame;
2012
+
2013
+ debug_check_started();
2014
+ frame = optional_frame_position(argc, argv);
2015
+ Data_Get_Struct(self, debug_context_t, debug_context);
2016
+
2017
+ debug_frame = GET_FRAME;
2018
+ if (debug_frame->dead)
2019
+ return debug_frame->info.copy.locals;
2020
+ else
2021
+ return context_copy_locals(debug_context, debug_frame, self);
2022
+ }
2023
+
2024
+ /*
2025
+ * call-seq:
2026
+ * context.frame_args(frame_position=0) -> list
2027
+ *
2028
+ * Returns frame's argument parameters
2029
+ */
2030
+ static VALUE
2031
+ context_frame_args(int argc, VALUE *argv, VALUE self)
2032
+ {
2033
+ VALUE frame;
2034
+ debug_context_t *debug_context;
2035
+ debug_frame_t *debug_frame;
2036
+
2037
+ debug_check_started();
2038
+ frame = optional_frame_position(argc, argv);
2039
+ Data_Get_Struct(self, debug_context_t, debug_context);
2040
+
2041
+ debug_frame = GET_FRAME;
2042
+ if (debug_frame->dead)
2043
+ return debug_frame->info.copy.args;
2044
+ else
2045
+ return context_copy_args(debug_frame);
2046
+ }
2047
+
2048
+ /*
2049
+ * call-seq:
2050
+ * context.frame_self(frame_postion=0) -> obj
2051
+ *
2052
+ * Returns self object of the frame.
2053
+ */
2054
+ static VALUE
2055
+ context_frame_self(int argc, VALUE *argv, VALUE self)
2056
+ {
2057
+ VALUE frame;
2058
+ debug_context_t *debug_context;
2059
+ debug_frame_t *debug_frame;
2060
+
2061
+ debug_check_started();
2062
+ frame = optional_frame_position(argc, argv);
2063
+ Data_Get_Struct(self, debug_context_t, debug_context);
2064
+
2065
+ debug_frame = GET_FRAME;
2066
+ return(debug_frame->self);
2067
+ }
2068
+
2069
+ /*
2070
+ * call-seq:
2071
+ * context.frame_class(frame_position) -> obj
2072
+ *
2073
+ * Returns the real class of the frame.
2074
+ * It could be different than context.frame_self(frame).class
2075
+ */
2076
+ static VALUE
2077
+ context_frame_class(int argc, VALUE *argv, VALUE self)
2078
+ {
2079
+ VALUE klass;
2080
+ VALUE frame;
2081
+ debug_context_t *debug_context;
2082
+ debug_frame_t *debug_frame;
2083
+ rb_control_frame_t *cfp;
2084
+
2085
+ debug_check_started();
2086
+ frame = optional_frame_position(argc, argv);
2087
+ Data_Get_Struct(self, debug_context_t, debug_context);
2088
+
2089
+ debug_frame = GET_FRAME;
2090
+
2091
+ cfp = debug_frame->info.runtime.cfp;
2092
+
2093
+ klass = real_class(cfp->iseq->klass);
2094
+ if(TYPE(klass) == T_CLASS || TYPE(klass) == T_MODULE)
2095
+ return klass;
2096
+ return Qnil;
2097
+ }
2098
+
2099
+
2100
+ /*
2101
+ * call-seq:
2102
+ * context.stack_size-> int
2103
+ *
2104
+ * Returns the size of the context stack.
2105
+ */
2106
+ static VALUE
2107
+ context_stack_size(VALUE self)
2108
+ {
2109
+ debug_context_t *debug_context;
2110
+
2111
+ debug_check_started();
2112
+ Data_Get_Struct(self, debug_context_t, debug_context);
2113
+
2114
+ return INT2FIX(debug_context->stack_size);
2115
+ }
2116
+
2117
+ /*
2118
+ * call-seq:
2119
+ * context.thread -> thread
2120
+ *
2121
+ * Returns a thread this context is associated with.
2122
+ */
2123
+ static VALUE
2124
+ context_thread(VALUE self)
2125
+ {
2126
+ debug_context_t *debug_context;
2127
+
2128
+ debug_check_started();
2129
+ Data_Get_Struct(self, debug_context_t, debug_context);
2130
+
2131
+ return(id2ref(debug_context->thread_id));
2132
+ }
2133
+
2134
+ /*
2135
+ * call-seq:
2136
+ * context.thnum -> int
2137
+ *
2138
+ * Returns the context's number.
2139
+ */
2140
+ static VALUE
2141
+ context_thnum(VALUE self)
2142
+ {
2143
+ debug_context_t *debug_context;
2144
+
2145
+ debug_check_started();
2146
+ Data_Get_Struct(self, debug_context_t, debug_context);
2147
+
2148
+ return INT2FIX(debug_context->thnum);
2149
+ }
2150
+
2151
+ static void
2152
+ context_suspend_0(debug_context_t *debug_context)
2153
+ {
2154
+ VALUE status;
2155
+
2156
+ status = rb_funcall(context_thread_0(debug_context), rb_intern("status"), 0);
2157
+ if(rb_str_cmp(status, rb_str_new2("run")) == 0)
2158
+ CTX_FL_SET(debug_context, CTX_FL_WAS_RUNNING);
2159
+ else if(rb_str_cmp(status, rb_str_new2("sleep")) == 0)
2160
+ CTX_FL_UNSET(debug_context, CTX_FL_WAS_RUNNING);
2161
+ else
2162
+ return;
2163
+ CTX_FL_SET(debug_context, CTX_FL_SUSPEND);
2164
+ }
2165
+
2166
+ static void
2167
+ context_resume_0(debug_context_t *debug_context)
2168
+ {
2169
+ if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
2170
+ return;
2171
+ CTX_FL_UNSET(debug_context, CTX_FL_SUSPEND);
2172
+ if(CTX_FL_TEST(debug_context, CTX_FL_WAS_RUNNING))
2173
+ rb_thread_wakeup(context_thread_0(debug_context));
2174
+ }
2175
+
2176
+ /*
2177
+ * call-seq:
2178
+ * context.suspend -> nil
2179
+ *
2180
+ * Suspends the thread when it is running.
2181
+ */
2182
+ static VALUE
2183
+ context_suspend(VALUE self)
2184
+ {
2185
+ debug_context_t *debug_context;
2186
+
2187
+ debug_check_started();
2188
+
2189
+ Data_Get_Struct(self, debug_context_t, debug_context);
2190
+ if(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
2191
+ rb_raise(rb_eRuntimeError, "Already suspended.");
2192
+ context_suspend_0(debug_context);
2193
+ return Qnil;
2194
+ }
2195
+
2196
+ /*
2197
+ * call-seq:
2198
+ * context.suspended? -> bool
2199
+ *
2200
+ * Returns +true+ if the thread is suspended by debugger.
2201
+ */
2202
+ static VALUE
2203
+ context_is_suspended(VALUE self)
2204
+ {
2205
+ debug_context_t *debug_context;
2206
+
2207
+ debug_check_started();
2208
+
2209
+ Data_Get_Struct(self, debug_context_t, debug_context);
2210
+ return CTX_FL_TEST(debug_context, CTX_FL_SUSPEND) ? Qtrue : Qfalse;
2211
+ }
2212
+
2213
+ /*
2214
+ * call-seq:
2215
+ * context.resume -> nil
2216
+ *
2217
+ * Resumes the thread from the suspended mode.
2218
+ */
2219
+ static VALUE
2220
+ context_resume(VALUE self)
2221
+ {
2222
+ debug_context_t *debug_context;
2223
+
2224
+ debug_check_started();
2225
+
2226
+ Data_Get_Struct(self, debug_context_t, debug_context);
2227
+ if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
2228
+ rb_raise(rb_eRuntimeError, "Thread is not suspended.");
2229
+ context_resume_0(debug_context);
2230
+ return Qnil;
2231
+ }
2232
+
2233
+ /*
2234
+ * call-seq:
2235
+ * context.tracing -> bool
2236
+ *
2237
+ * Returns the tracing flag for the current context.
2238
+ */
2239
+ static VALUE
2240
+ context_tracing(VALUE self)
2241
+ {
2242
+ debug_context_t *debug_context;
2243
+
2244
+ debug_check_started();
2245
+
2246
+ Data_Get_Struct(self, debug_context_t, debug_context);
2247
+ return CTX_FL_TEST(debug_context, CTX_FL_TRACING) ? Qtrue : Qfalse;
2248
+ }
2249
+
2250
+ /*
2251
+ * call-seq:
2252
+ * context.tracing = bool
2253
+ *
2254
+ * Controls the tracing for this context.
2255
+ */
2256
+ static VALUE
2257
+ context_set_tracing(VALUE self, VALUE value)
2258
+ {
2259
+ debug_context_t *debug_context;
2260
+
2261
+ debug_check_started();
2262
+
2263
+ Data_Get_Struct(self, debug_context_t, debug_context);
2264
+ if(RTEST(value))
2265
+ CTX_FL_SET(debug_context, CTX_FL_TRACING);
2266
+ else
2267
+ CTX_FL_UNSET(debug_context, CTX_FL_TRACING);
2268
+ return value;
2269
+ }
2270
+
2271
+ /*
2272
+ * call-seq:
2273
+ * context.ignored? -> bool
2274
+ *
2275
+ * Returns the ignore flag for the current context.
2276
+ */
2277
+ static VALUE
2278
+ context_ignored(VALUE self)
2279
+ {
2280
+ debug_context_t *debug_context;
2281
+
2282
+ debug_check_started();
2283
+
2284
+ Data_Get_Struct(self, debug_context_t, debug_context);
2285
+ return CTX_FL_TEST(debug_context, CTX_FL_IGNORE) ? Qtrue : Qfalse;
2286
+ }
2287
+
2288
+ /*
2289
+ * call-seq:
2290
+ * context.dead? -> bool
2291
+ *
2292
+ * Returns +true+ if context doesn't represent a live context and is created
2293
+ * during post-mortem exception handling.
2294
+ */
2295
+ static VALUE
2296
+ context_dead(VALUE self)
2297
+ {
2298
+ debug_context_t *debug_context;
2299
+
2300
+ debug_check_started();
2301
+
2302
+ Data_Get_Struct(self, debug_context_t, debug_context);
2303
+ return CTX_FL_TEST(debug_context, CTX_FL_DEAD) ? Qtrue : Qfalse;
2304
+ }
2305
+
2306
+ /*
2307
+ * call-seq:
2308
+ * context.stop_reason -> sym
2309
+ *
2310
+ * Returns the reason for the stop. It maybe of the following values:
2311
+ * :initial, :step, :breakpoint, :catchpoint, :post-mortem
2312
+ */
2313
+ static VALUE
2314
+ context_stop_reason(VALUE self)
2315
+ {
2316
+ debug_context_t *debug_context;
2317
+ const char * sym_name;
2318
+
2319
+ debug_check_started();
2320
+
2321
+ Data_Get_Struct(self, debug_context_t, debug_context);
2322
+
2323
+ switch(debug_context->stop_reason)
2324
+ {
2325
+ case CTX_STOP_STEP:
2326
+ sym_name = "step";
2327
+ break;
2328
+ case CTX_STOP_BREAKPOINT:
2329
+ sym_name = "breakpoint";
2330
+ break;
2331
+ case CTX_STOP_CATCHPOINT:
2332
+ sym_name = "catchpoint";
2333
+ break;
2334
+ case CTX_STOP_NONE:
2335
+ default:
2336
+ sym_name = "none";
2337
+ }
2338
+ if(CTX_FL_TEST(debug_context, CTX_FL_DEAD))
2339
+ sym_name = "post-mortem";
2340
+
2341
+ return ID2SYM(rb_intern(sym_name));
2342
+ }
2343
+
2344
+ static rb_control_frame_t *
2345
+ FUNC_FASTCALL(do_jump)(rb_thread_t *th, rb_control_frame_t *cfp)
2346
+ {
2347
+ VALUE context;
2348
+ debug_context_t *debug_context;
2349
+ rb_control_frame_t *jump_cfp;
2350
+ VALUE *jump_pc;
2351
+
2352
+ thread_context_lookup(th->self, &context, &debug_context, 0);
2353
+ if (debug_context == NULL)
2354
+ rb_raise(rb_eRuntimeError, "Lost context in jump");
2355
+ cfp->pc[-2] = debug_context->saved_jump_ins[0];
2356
+ cfp->pc[-1] = debug_context->saved_jump_ins[1];
2357
+
2358
+ if ((debug_context->jump_pc < debug_context->jump_cfp->iseq->iseq_encoded) ||
2359
+ (debug_context->jump_pc >= debug_context->jump_cfp->iseq->iseq_encoded + debug_context->jump_cfp->iseq->iseq_size))
2360
+ rb_raise(rb_eRuntimeError, "Invalid jump PC target");
2361
+
2362
+ jump_cfp = debug_context->jump_cfp;
2363
+ jump_pc = debug_context->jump_pc;
2364
+ debug_context->jump_pc = NULL;
2365
+ debug_context->jump_cfp = NULL;
2366
+ debug_context->last_line = 0;
2367
+ debug_context->last_file = NULL;
2368
+ debug_context->stop_next = 1;
2369
+
2370
+ if (cfp < jump_cfp)
2371
+ {
2372
+ /* save all intermediate-frame catch tables
2373
+ +1 for target frame
2374
+ +1 for array terminator
2375
+ */
2376
+ int frames = jump_cfp - cfp + 2;
2377
+ debug_context->old_iseq_catch = (iseq_catch_t*)malloc(frames * sizeof(iseq_catch_t));
2378
+ MEMZERO(debug_context->old_iseq_catch, iseq_catch_t, frames);
2379
+ frames = 0;
2380
+ do
2381
+ {
2382
+ if (cfp->iseq != NULL)
2383
+ {
2384
+ debug_context->old_iseq_catch[frames].iseq = cfp->iseq;
2385
+ debug_context->old_iseq_catch[frames].catch_table = cfp->iseq->catch_table;
2386
+ debug_context->old_iseq_catch[frames].catch_table_size = cfp->iseq->catch_table_size;
2387
+ cfp->iseq->catch_table = NULL;
2388
+ cfp->iseq->catch_table_size = 0;
2389
+
2390
+ frames++;
2391
+ }
2392
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
2393
+ } while (cfp <= jump_cfp);
2394
+
2395
+ jump_cfp->iseq->catch_table_size = 1;
2396
+ jump_cfp->iseq->catch_table =
2397
+ create_catch_table(debug_context, jump_pc - jump_cfp->iseq->iseq_encoded);
2398
+ jump_cfp->iseq->catch_table->sp = -1;
2399
+
2400
+ JUMP_TAG(TAG_RAISE);
2401
+ }
2402
+ else if (cfp > jump_cfp)
2403
+ rb_raise(rb_eRuntimeError, "Invalid jump frame target");
2404
+
2405
+ cfp->pc = jump_pc;
2406
+ return(cfp);
2407
+ }
2408
+
2409
+ /*
2410
+ * call-seq:
2411
+ * context.jump(line, file) -> bool
2412
+ *
2413
+ * Returns +true+ if jump to +line+ in filename +file+ was successful.
2414
+ */
2415
+ static VALUE
2416
+ context_jump(VALUE self, VALUE line, VALUE file)
2417
+ {
2418
+ debug_context_t *debug_context;
2419
+ debug_frame_t *debug_frame;
2420
+ int i;
2421
+ rb_thread_t *th;
2422
+ rb_control_frame_t *cfp;
2423
+ rb_control_frame_t *cfp_end;
2424
+ rb_control_frame_t *cfp_start = NULL;
2425
+
2426
+ debug_check_started();
2427
+
2428
+ Data_Get_Struct(self, debug_context_t, debug_context);
2429
+ GetThreadPtr(context_thread_0(debug_context), th);
2430
+ debug_frame = get_top_frame(debug_context);
2431
+ if (debug_frame == NULL)
2432
+ rb_raise(rb_eRuntimeError, "No frames collected.");
2433
+
2434
+ line = FIX2INT(line);
2435
+
2436
+ /* find topmost frame of the debugged code */
2437
+ cfp = th->cfp;
2438
+ cfp_end = RUBY_VM_END_CONTROL_FRAME(th);
2439
+ while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, cfp_end))
2440
+ {
2441
+ if (cfp->pc == debug_frame->info.runtime.last_pc)
2442
+ {
2443
+ cfp_start = cfp;
2444
+ if ((cfp->pc - cfp->iseq->iseq_encoded) >= (cfp->iseq->iseq_size - 1))
2445
+ return(INT2FIX(1)); /* no space for opt_call_c_function hijack */
2446
+ break;
2447
+ }
2448
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
2449
+ }
2450
+ if (cfp_start == NULL)
2451
+ return(INT2FIX(2)); /* couldn't find frame; should never happen */
2452
+
2453
+ /* find target frame to jump to */
2454
+ while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, cfp_end))
2455
+ {
2456
+ #ifdef HAVE_RB_ISEQ_T_LOCATION
2457
+ if ((cfp->iseq != NULL) && (rb_str_cmp(file, cfp->iseq->location.path) == 0))
2458
+ #else
2459
+ if ((cfp->iseq != NULL) && (rb_str_cmp(file, cfp->iseq->filename) == 0))
2460
+ #endif
2461
+ {
2462
+ #ifdef HAVE_RB_ISEQ_T_LINE_INFO_SIZE
2463
+ for (i = 0; i < cfp->iseq->line_info_size; i++)
2464
+ #else
2465
+ for (i = 0; i < cfp->iseq->insn_info_size; i++)
2466
+ #endif
2467
+ {
2468
+ #ifdef HAVE_RB_ISEQ_T_LINE_INFO_SIZE
2469
+ if (cfp->iseq->line_info_table[i].line_no != line)
2470
+ #else
2471
+ if (cfp->iseq->insn_info_table[i].line_no != line)
2472
+ #endif
2473
+ continue;
2474
+
2475
+ /* hijack the currently running code so that we can change the frame PC */
2476
+ debug_context->saved_jump_ins[0] = cfp_start->pc[0];
2477
+ debug_context->saved_jump_ins[1] = cfp_start->pc[1];
2478
+ cfp_start->pc[0] = opt_call_c_function;
2479
+ cfp_start->pc[1] = (VALUE)do_jump;
2480
+
2481
+ debug_context->jump_cfp = cfp;
2482
+ #ifdef HAVE_RB_ISEQ_T_LINE_INFO_SIZE
2483
+ debug_context->jump_pc =
2484
+ cfp->iseq->iseq_encoded + cfp->iseq->line_info_table[i].position;
2485
+ #else
2486
+ debug_context->jump_pc =
2487
+ cfp->iseq->iseq_encoded + cfp->iseq->insn_info_table[i].position;
2488
+ #endif
2489
+
2490
+ return(INT2FIX(0)); /* success */
2491
+ }
2492
+ }
2493
+
2494
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
2495
+ }
2496
+
2497
+ return(INT2FIX(3)); /* couldn't find a line and file frame match */
2498
+ }
2499
+
2500
+ /*
2501
+ * call-seq:
2502
+ * context.break -> bool
2503
+ *
2504
+ * Returns +true+ if context is currently running and set flag to break it at next line
2505
+ */
2506
+ static VALUE
2507
+ context_pause(VALUE self)
2508
+ {
2509
+ debug_context_t *debug_context;
2510
+ rb_thread_t *th;
2511
+
2512
+ debug_check_started();
2513
+
2514
+ Data_Get_Struct(self, debug_context_t, debug_context);
2515
+ if (CTX_FL_TEST(debug_context, CTX_FL_DEAD))
2516
+ return(Qfalse);
2517
+
2518
+ GetThreadPtr(context_thread_0(debug_context), th);
2519
+ if (th == GET_THREAD2())
2520
+ return(Qfalse);
2521
+
2522
+ debug_context->thread_pause = 1;
2523
+ return(Qtrue);
2524
+ }
2525
+
2526
+ /*
2527
+ * Document-class: Context
2528
+ *
2529
+ * == Summary
2530
+ *
2531
+ * Debugger keeps a single instance of this class for each Ruby thread.
2532
+ */
2533
+ static void
2534
+ Init_context(void)
2535
+ {
2536
+ cContext = rb_define_class_under(mDebugger, "Context", rb_cObject);
2537
+ rb_define_method(cContext, "stop_next=", context_stop_next, -1);
2538
+ rb_define_method(cContext, "step", context_stop_next, -1);
2539
+ rb_define_method(cContext, "step_over", context_step_over, -1);
2540
+ rb_define_method(cContext, "stop_frame=", context_stop_frame, 1);
2541
+ rb_define_method(cContext, "thread", context_thread, 0);
2542
+ rb_define_method(cContext, "thnum", context_thnum, 0);
2543
+ rb_define_method(cContext, "stop_reason", context_stop_reason, 0);
2544
+ rb_define_method(cContext, "suspend", context_suspend, 0);
2545
+ rb_define_method(cContext, "suspended?", context_is_suspended, 0);
2546
+ rb_define_method(cContext, "resume", context_resume, 0);
2547
+ rb_define_method(cContext, "tracing", context_tracing, 0);
2548
+ rb_define_method(cContext, "tracing=", context_set_tracing, 1);
2549
+ rb_define_method(cContext, "ignored?", context_ignored, 0);
2550
+ rb_define_method(cContext, "frame_args", context_frame_args, -1);
2551
+ rb_define_method(cContext, "frame_args_info", context_frame_args_info, -1);
2552
+ rb_define_method(cContext, "frame_binding", context_frame_binding, -1);
2553
+ rb_define_method(cContext, "frame_class", context_frame_class, -1);
2554
+ rb_define_method(cContext, "frame_file", context_frame_file, -1);
2555
+ rb_define_method(cContext, "frame_id", context_frame_id, -1);
2556
+ rb_define_method(cContext, "frame_line", context_frame_line, -1);
2557
+ rb_define_method(cContext, "frame_locals", context_frame_locals, -1);
2558
+ rb_define_method(cContext, "frame_method", context_frame_id, -1);
2559
+ rb_define_method(cContext, "frame_self", context_frame_self, -1);
2560
+ rb_define_method(cContext, "stack_size", context_stack_size, 0);
2561
+ rb_define_method(cContext, "dead?", context_dead, 0);
2562
+ rb_define_method(cContext, "breakpoint",
2563
+ context_breakpoint, 0); /* in breakpoint.c */
2564
+ rb_define_method(cContext, "set_breakpoint",
2565
+ context_set_breakpoint, -1); /* in breakpoint.c */
2566
+ rb_define_method(cContext, "jump", context_jump, 2);
2567
+ rb_define_method(cContext, "pause", context_pause, 0);
2568
+ }
2569
+
2570
+ /*
2571
+ * call-seq:
2572
+ * Debugger.breakpoints -> array
2573
+ *
2574
+ * Returns an array of breakpoints.
2575
+ */
2576
+ static VALUE
2577
+ debug_breakpoints(VALUE self)
2578
+ {
2579
+ debug_check_started();
2580
+
2581
+ return rdebug_breakpoints;
2582
+ }
2583
+
2584
+ /*
2585
+ * call-seq:
2586
+ * Debugger.add_breakpoint(source, pos, condition = nil) -> breakpoint
2587
+ *
2588
+ * Adds a new breakpoint.
2589
+ * <i>source</i> is a name of a file or a class.
2590
+ * <i>pos</i> is a line number or a method name if <i>source</i> is a class name.
2591
+ * <i>condition</i> is a string which is evaluated to +true+ when this breakpoint
2592
+ * is activated.
2593
+ */
2594
+ static VALUE
2595
+ debug_add_breakpoint(int argc, VALUE *argv, VALUE self)
2596
+ {
2597
+ VALUE result;
2598
+
2599
+ debug_check_started();
2600
+
2601
+ result = create_breakpoint_from_args(argc, argv, ++bkp_count);
2602
+ rb_ary_push(rdebug_breakpoints, result);
2603
+ return result;
2604
+ }
2605
+
2606
+ /*
2607
+ * Document-class: Debugger
2608
+ *
2609
+ * == Summary
2610
+ *
2611
+ * This is a singleton class allows controlling the debugger. Use it to start/stop debugger,
2612
+ * set/remove breakpoints, etc.
2613
+ */
2614
+ void
2615
+ Init_ruby_debug(void)
2616
+ {
2617
+ rb_iseq_t iseq;
2618
+ iseq.iseq = &opt_call_c_function;
2619
+ iseq.iseq_size = 1;
2620
+ iseq.iseq_encoded = NULL;
2621
+
2622
+ opt_call_c_function = (VALUE)BIN(opt_call_c_function);
2623
+ rb_iseq_translate_threaded_code(&iseq);
2624
+ if (iseq.iseq_encoded != iseq.iseq)
2625
+ {
2626
+ opt_call_c_function = iseq.iseq_encoded[0];
2627
+ xfree(iseq.iseq_encoded);
2628
+ }
2629
+
2630
+ mDebugger = rb_define_module("Debugger");
2631
+ rb_define_const(mDebugger, "VERSION", rb_str_new2(DEBUG_VERSION));
2632
+ rb_define_module_function(mDebugger, "start_", debug_start, 0);
2633
+ rb_define_module_function(mDebugger, "stop", debug_stop, 0);
2634
+ rb_define_module_function(mDebugger, "started?", debug_is_started, 0);
2635
+ rb_define_module_function(mDebugger, "breakpoints", debug_breakpoints, 0);
2636
+ rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, -1);
2637
+ rb_define_module_function(mDebugger, "remove_breakpoint",
2638
+ rdebug_remove_breakpoint,
2639
+ 1); /* in breakpoint.c */
2640
+ rb_define_module_function(mDebugger, "add_catchpoint",
2641
+ rdebug_add_catchpoint, 1); /* in breakpoint.c */
2642
+ rb_define_module_function(mDebugger, "catchpoints",
2643
+ debug_catchpoints, 0); /* in breakpoint.c */
2644
+ rb_define_module_function(mDebugger, "last_context", debug_last_interrupted, 0);
2645
+ rb_define_module_function(mDebugger, "contexts", debug_contexts, 0);
2646
+ rb_define_module_function(mDebugger, "current_context", debug_current_context, 0);
2647
+ rb_define_module_function(mDebugger, "thread_context", debug_thread_context, 1);
2648
+ rb_define_module_function(mDebugger, "suspend", debug_suspend, 0);
2649
+ rb_define_module_function(mDebugger, "resume", debug_resume, 0);
2650
+ rb_define_module_function(mDebugger, "tracing", debug_tracing, 0);
2651
+ rb_define_module_function(mDebugger, "tracing=", debug_set_tracing, 1);
2652
+ rb_define_module_function(mDebugger, "debug_load", debug_debug_load, -1);
2653
+ rb_define_module_function(mDebugger, "skip", debug_skip, 0);
2654
+ rb_define_module_function(mDebugger, "debug_at_exit", debug_at_exit, 0);
2655
+ rb_define_module_function(mDebugger, "post_mortem?", debug_post_mortem, 0);
2656
+ rb_define_module_function(mDebugger, "post_mortem=", debug_set_post_mortem, 1);
2657
+ rb_define_module_function(mDebugger, "keep_frame_binding?",
2658
+ debug_keep_frame_binding, 0);
2659
+ rb_define_module_function(mDebugger, "keep_frame_binding=",
2660
+ debug_set_keep_frame_binding, 1);
2661
+ rb_define_module_function(mDebugger, "track_frame_args?",
2662
+ debug_track_frame_args, 0);
2663
+ rb_define_module_function(mDebugger, "track_frame_args=",
2664
+ debug_set_track_frame_args, 1);
2665
+ rb_define_module_function(mDebugger, "debug", debug_debug, 0);
2666
+ rb_define_module_function(mDebugger, "debug=", debug_set_debug, 1);
2667
+
2668
+ cThreadsTable = rb_define_class_under(mDebugger, "ThreadsTable", rb_cObject);
2669
+
2670
+ cDebugThread = rb_define_class_under(mDebugger, "DebugThread", rb_cThread);
2671
+ rb_define_singleton_method(cDebugThread, "inherited",
2672
+ debug_thread_inherited, 1);
2673
+
2674
+ Init_context();
2675
+ Init_breakpoint();
2676
+
2677
+ idAtBreakpoint = rb_intern("at_breakpoint");
2678
+ idAtCatchpoint = rb_intern("at_catchpoint");
2679
+ idAtLine = rb_intern("at_line");
2680
+ idAtReturn = rb_intern("at_return");
2681
+ idAtTracing = rb_intern("at_tracing");
2682
+ idList = rb_intern("list");
2683
+
2684
+ rb_mObjectSpace = rb_const_get(rb_mKernel, rb_intern("ObjectSpace"));
2685
+
2686
+ rb_global_variable(&last_context);
2687
+ rb_global_variable(&last_thread);
2688
+ rb_global_variable(&locker);
2689
+ rb_global_variable(&rdebug_breakpoints);
2690
+ rb_global_variable(&rdebug_catchpoints);
2691
+ rb_global_variable(&rdebug_threads_tbl);
2692
+ }