needy_debugger 1.4.0

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