debugger2 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +3 -0
  4. data/AUTHORS +10 -0
  5. data/CHANGELOG.md +65 -0
  6. data/CONTRIBUTING.md +1 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +23 -0
  9. data/OLDER_CHANGELOG +334 -0
  10. data/OLD_CHANGELOG +5655 -0
  11. data/OLD_README +122 -0
  12. data/README.md +108 -0
  13. data/Rakefile +78 -0
  14. data/bin/rdebug +397 -0
  15. data/debugger2.gemspec +29 -0
  16. data/doc/.cvsignore +42 -0
  17. data/doc/Makefile.am +63 -0
  18. data/doc/emacs-notes.txt +38 -0
  19. data/doc/hanoi.rb +35 -0
  20. data/doc/primes.rb +28 -0
  21. data/doc/rdebug-emacs.texi +1030 -0
  22. data/doc/ruby-debug.texi +3791 -0
  23. data/doc/test-tri2.rb +18 -0
  24. data/doc/tri3.rb +8 -0
  25. data/doc/triangle.rb +12 -0
  26. data/emacs/Makefile.am +130 -0
  27. data/emacs/rdebug-annotate.el +385 -0
  28. data/emacs/rdebug-breaks.el +407 -0
  29. data/emacs/rdebug-cmd.el +92 -0
  30. data/emacs/rdebug-core.el +502 -0
  31. data/emacs/rdebug-dbg.el +62 -0
  32. data/emacs/rdebug-error.el +79 -0
  33. data/emacs/rdebug-fns.el +111 -0
  34. data/emacs/rdebug-frames.el +230 -0
  35. data/emacs/rdebug-gud.el +242 -0
  36. data/emacs/rdebug-help.el +104 -0
  37. data/emacs/rdebug-info.el +83 -0
  38. data/emacs/rdebug-layouts.el +180 -0
  39. data/emacs/rdebug-locring.el +118 -0
  40. data/emacs/rdebug-output.el +106 -0
  41. data/emacs/rdebug-regexp.el +118 -0
  42. data/emacs/rdebug-secondary.el +260 -0
  43. data/emacs/rdebug-shortkey.el +175 -0
  44. data/emacs/rdebug-source.el +568 -0
  45. data/emacs/rdebug-track.el +392 -0
  46. data/emacs/rdebug-varbuf.el +150 -0
  47. data/emacs/rdebug-vars.el +125 -0
  48. data/emacs/rdebug-watch.el +132 -0
  49. data/emacs/rdebug.el +326 -0
  50. data/emacs/test/elk-test.el +242 -0
  51. data/emacs/test/test-annotate.el +103 -0
  52. data/emacs/test/test-cmd.el +116 -0
  53. data/emacs/test/test-core.el +104 -0
  54. data/emacs/test/test-fns.el +65 -0
  55. data/emacs/test/test-frames.el +62 -0
  56. data/emacs/test/test-gud.el +35 -0
  57. data/emacs/test/test-indent.el +58 -0
  58. data/emacs/test/test-regexp.el +144 -0
  59. data/emacs/test/test-shortkey.el +61 -0
  60. data/ext/ruby_debug/breakpoint.c +630 -0
  61. data/ext/ruby_debug/extconf.rb +11 -0
  62. data/ext/ruby_debug/ruby_debug.c +2203 -0
  63. data/ext/ruby_debug/ruby_debug.h +151 -0
  64. data/lib/debugger.rb +5 -0
  65. data/lib/debugger/version.rb +5 -0
  66. data/lib/debugger2.rb +6 -0
  67. data/lib/ruby-debug-base.rb +307 -0
  68. data/lib/ruby-debug.rb +176 -0
  69. data/lib/ruby-debug/command.rb +227 -0
  70. data/lib/ruby-debug/commands/breakpoints.rb +153 -0
  71. data/lib/ruby-debug/commands/catchpoint.rb +55 -0
  72. data/lib/ruby-debug/commands/condition.rb +49 -0
  73. data/lib/ruby-debug/commands/continue.rb +38 -0
  74. data/lib/ruby-debug/commands/control.rb +107 -0
  75. data/lib/ruby-debug/commands/display.rb +120 -0
  76. data/lib/ruby-debug/commands/edit.rb +48 -0
  77. data/lib/ruby-debug/commands/enable.rb +202 -0
  78. data/lib/ruby-debug/commands/eval.rb +176 -0
  79. data/lib/ruby-debug/commands/finish.rb +42 -0
  80. data/lib/ruby-debug/commands/frame.rb +301 -0
  81. data/lib/ruby-debug/commands/help.rb +56 -0
  82. data/lib/ruby-debug/commands/info.rb +467 -0
  83. data/lib/ruby-debug/commands/irb.rb +123 -0
  84. data/lib/ruby-debug/commands/jump.rb +66 -0
  85. data/lib/ruby-debug/commands/kill.rb +51 -0
  86. data/lib/ruby-debug/commands/list.rb +94 -0
  87. data/lib/ruby-debug/commands/method.rb +84 -0
  88. data/lib/ruby-debug/commands/quit.rb +39 -0
  89. data/lib/ruby-debug/commands/reload.rb +40 -0
  90. data/lib/ruby-debug/commands/save.rb +90 -0
  91. data/lib/ruby-debug/commands/set.rb +223 -0
  92. data/lib/ruby-debug/commands/show.rb +247 -0
  93. data/lib/ruby-debug/commands/skip.rb +35 -0
  94. data/lib/ruby-debug/commands/source.rb +36 -0
  95. data/lib/ruby-debug/commands/stepping.rb +81 -0
  96. data/lib/ruby-debug/commands/threads.rb +189 -0
  97. data/lib/ruby-debug/commands/tmate.rb +36 -0
  98. data/lib/ruby-debug/commands/trace.rb +57 -0
  99. data/lib/ruby-debug/commands/variables.rb +199 -0
  100. data/lib/ruby-debug/debugger.rb +5 -0
  101. data/lib/ruby-debug/helper.rb +69 -0
  102. data/lib/ruby-debug/interface.rb +232 -0
  103. data/lib/ruby-debug/processor.rb +474 -0
  104. data/man/rdebug.1 +241 -0
  105. data/old_scripts/Makefile.am +14 -0
  106. data/old_scripts/README.md +2 -0
  107. data/old_scripts/autogen.sh +4 -0
  108. data/old_scripts/configure.ac +12 -0
  109. data/old_scripts/rdbg.rb +33 -0
  110. data/old_scripts/runner.sh +7 -0
  111. data/old_scripts/svn2cl_usermap +3 -0
  112. data/test/.cvsignore +1 -0
  113. data/test/breakpoints_test.rb +366 -0
  114. data/test/conditions_test.rb +77 -0
  115. data/test/continue_test.rb +28 -0
  116. data/test/display_test.rb +143 -0
  117. data/test/edit_test.rb +55 -0
  118. data/test/eval_test.rb +94 -0
  119. data/test/examples/breakpoint1.rb +15 -0
  120. data/test/examples/breakpoint2.rb +7 -0
  121. data/test/examples/conditions.rb +4 -0
  122. data/test/examples/continue.rb +4 -0
  123. data/test/examples/display.rb +5 -0
  124. data/test/examples/edit.rb +3 -0
  125. data/test/examples/edit2.rb +3 -0
  126. data/test/examples/eval.rb +4 -0
  127. data/test/examples/finish.rb +20 -0
  128. data/test/examples/frame.rb +31 -0
  129. data/test/examples/help.rb +2 -0
  130. data/test/examples/info.rb +48 -0
  131. data/test/examples/info2.rb +3 -0
  132. data/test/examples/irb.rb +6 -0
  133. data/test/examples/jump.rb +14 -0
  134. data/test/examples/kill.rb +2 -0
  135. data/test/examples/list.rb +12 -0
  136. data/test/examples/method.rb +15 -0
  137. data/test/examples/post_mortem.rb +19 -0
  138. data/test/examples/quit.rb +2 -0
  139. data/test/examples/reload.rb +6 -0
  140. data/test/examples/restart.rb +6 -0
  141. data/test/examples/save.rb +3 -0
  142. data/test/examples/set.rb +3 -0
  143. data/test/examples/set_annotate.rb +12 -0
  144. data/test/examples/settings.rb +1 -0
  145. data/test/examples/show.rb +2 -0
  146. data/test/examples/source.rb +3 -0
  147. data/test/examples/stepping.rb +21 -0
  148. data/test/examples/thread.rb +32 -0
  149. data/test/examples/tmate.rb +10 -0
  150. data/test/examples/trace.rb +7 -0
  151. data/test/examples/trace_threads.rb +20 -0
  152. data/test/examples/variables.rb +26 -0
  153. data/test/finish_test.rb +49 -0
  154. data/test/frame_test.rb +140 -0
  155. data/test/help_test.rb +51 -0
  156. data/test/info_test.rb +326 -0
  157. data/test/irb_test.rb +82 -0
  158. data/test/jump_test.rb +70 -0
  159. data/test/kill_test.rb +49 -0
  160. data/test/list_test.rb +147 -0
  161. data/test/method_test.rb +72 -0
  162. data/test/post_mortem_test.rb +25 -0
  163. data/test/quit_test.rb +56 -0
  164. data/test/reload_test.rb +47 -0
  165. data/test/restart_test.rb +145 -0
  166. data/test/save_test.rb +94 -0
  167. data/test/set_test.rb +183 -0
  168. data/test/show_test.rb +294 -0
  169. data/test/source_test.rb +46 -0
  170. data/test/stepping_test.rb +122 -0
  171. data/test/support/breakpoint.rb +12 -0
  172. data/test/support/context.rb +14 -0
  173. data/test/support/matchers.rb +67 -0
  174. data/test/support/mocha_extensions.rb +71 -0
  175. data/test/support/processor.rb +7 -0
  176. data/test/support/test_dsl.rb +206 -0
  177. data/test/support/test_interface.rb +66 -0
  178. data/test/test_helper.rb +9 -0
  179. data/test/thread_test.rb +124 -0
  180. data/test/tmate_test.rb +45 -0
  181. data/test/trace_test.rb +156 -0
  182. data/test/variables_test.rb +116 -0
  183. metadata +319 -0
@@ -0,0 +1,11 @@
1
+ if RUBY_VERSION < "2.0"
2
+ STDERR.print("Ruby version is too old\n")
3
+ exit(1)
4
+ end
5
+
6
+ require 'mkmf'
7
+ # $optflags = '-O0'
8
+ CONFIG['optflags'] = '-O0'
9
+
10
+
11
+ create_makefile('ruby_debug')
@@ -0,0 +1,2203 @@
1
+ #include <stdio.h>
2
+ #include <ctype.h>
3
+ #include "ruby_debug.h"
4
+
5
+ #define DEBUG_VERSION "0.11"
6
+
7
+ static VALUE debug_stop(VALUE);
8
+ static void context_suspend_0(debug_context_t *);
9
+ static void context_resume_0(debug_context_t *);
10
+
11
+ typedef struct {
12
+ st_table *tbl;
13
+ } threads_table_t;
14
+
15
+ static VALUE tracing = Qfalse;
16
+ static VALUE locker = Qnil;
17
+ static VALUE post_mortem = Qfalse;
18
+ static VALUE keep_frame_binding = Qfalse;
19
+ static VALUE track_frame_args = Qfalse;
20
+
21
+ static VALUE debug_debugger = Qfalse;
22
+ static const VALUE debug_debugger_stack_size = Qfalse;
23
+
24
+ static VALUE last_context = Qnil;
25
+ static VALUE last_thread = Qnil;
26
+ static VALUE tracepoints = Qnil;
27
+
28
+ static debug_context_t *last_debug_context = NULL;
29
+
30
+ VALUE rdebug_threads_tbl = Qnil; /* Context for each of the threads */
31
+ VALUE mDebugger; /* Ruby Debugger Module object */
32
+
33
+ static VALUE cThreadsTable;
34
+ static VALUE cContext;
35
+ static VALUE cDebugThread;
36
+
37
+ static int start_count = 0;
38
+ static int thnum_max = 0;
39
+ static int bkp_count = 0;
40
+ static int last_debugged_thnum = -1;
41
+ static unsigned long last_check = 0;
42
+ static unsigned long hook_count = 0;
43
+
44
+ typedef struct locked_thread_t {
45
+ VALUE thread_id;
46
+ struct locked_thread_t *next;
47
+ } locked_thread_t;
48
+
49
+ static locked_thread_t *locked_head = NULL;
50
+ static locked_thread_t *locked_tail = NULL;
51
+
52
+ static void
53
+ reset_stepping_stop_points(debug_context_t *debug_context)
54
+ {
55
+ debug_context->dest_frame = -1;
56
+ debug_context->stop_line = -1;
57
+ debug_context->stop_next = -1;
58
+ }
59
+
60
+ static VALUE
61
+ ref2id(VALUE obj)
62
+ {
63
+ return obj;
64
+ }
65
+
66
+ static VALUE
67
+ id2ref(VALUE id)
68
+ {
69
+ return id;
70
+ }
71
+
72
+ static VALUE
73
+ context_thread_0(debug_context_t *debug_context)
74
+ {
75
+ return id2ref(debug_context->thread_id);
76
+ }
77
+
78
+ #define ruby_threadptr_data_type *threadptr_data_type()
79
+
80
+ #define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
81
+
82
+ static int
83
+ is_in_locked(VALUE thread_id)
84
+ {
85
+ locked_thread_t *node;
86
+
87
+ if(!locked_head)
88
+ return 0;
89
+
90
+ for(node = locked_head; node != locked_tail; node = node->next)
91
+ {
92
+ if(node->thread_id == thread_id) return 1;
93
+ }
94
+ return 0;
95
+ }
96
+
97
+ static void
98
+ add_to_locked(VALUE thread)
99
+ {
100
+ locked_thread_t *node;
101
+ VALUE thread_id = ref2id(thread);
102
+
103
+ if(is_in_locked(thread_id))
104
+ return;
105
+
106
+ node = ALLOC(locked_thread_t);
107
+ node->thread_id = thread_id;
108
+ node->next = NULL;
109
+ if(locked_tail)
110
+ locked_tail->next = node;
111
+ locked_tail = node;
112
+ if(!locked_head)
113
+ locked_head = node;
114
+ }
115
+
116
+ static VALUE
117
+ remove_from_locked(void)
118
+ {
119
+ VALUE thread;
120
+ locked_thread_t *node;
121
+
122
+ if(locked_head == NULL)
123
+ return Qnil;
124
+ node = locked_head;
125
+ locked_head = locked_head->next;
126
+ if(locked_tail == node)
127
+ locked_tail = NULL;
128
+ thread = id2ref(node->thread_id);
129
+ xfree(node);
130
+ return thread;
131
+ }
132
+
133
+ static int
134
+ threads_table_mark_keyvalue(st_data_t key, st_data_t value, st_data_t tbl)
135
+ {
136
+ VALUE thread = id2ref((VALUE)key);
137
+ if (!value)
138
+ return ST_CONTINUE;
139
+
140
+ rb_gc_mark((VALUE)value);
141
+ rb_gc_mark(thread);
142
+
143
+ return ST_CONTINUE;
144
+ }
145
+
146
+ static void
147
+ threads_table_mark(void* data)
148
+ {
149
+ threads_table_t *threads_table = (threads_table_t*)data;
150
+ st_table *tbl = threads_table->tbl;
151
+ st_foreach(tbl, threads_table_mark_keyvalue, (st_data_t)tbl);
152
+ }
153
+
154
+ static void
155
+ threads_table_free(void* data)
156
+ {
157
+ threads_table_t *threads_table = (threads_table_t*)data;
158
+ st_free_table(threads_table->tbl);
159
+ xfree(threads_table);
160
+ }
161
+
162
+ static VALUE
163
+ threads_table_create(void)
164
+ {
165
+ threads_table_t *threads_table;
166
+
167
+ threads_table = ALLOC(threads_table_t);
168
+ threads_table->tbl = st_init_numtable();
169
+ return Data_Wrap_Struct(cThreadsTable, threads_table_mark, threads_table_free, threads_table);
170
+ }
171
+
172
+ static void
173
+ threads_table_clear(VALUE table)
174
+ {
175
+ threads_table_t *threads_table;
176
+
177
+ Data_Get_Struct(table, threads_table_t, threads_table);
178
+ st_clear(threads_table->tbl);
179
+ }
180
+
181
+ static int
182
+ is_living_thread(VALUE thread)
183
+ {
184
+ return rb_funcall(thread, rb_intern("alive?"), 0) == Qtrue;
185
+ }
186
+
187
+ static int
188
+ threads_table_check_i(st_data_t key, st_data_t value, st_data_t dummy)
189
+ {
190
+ VALUE thread;
191
+
192
+ if(!value)
193
+ {
194
+ return ST_DELETE;
195
+ }
196
+ thread = id2ref((VALUE)key);
197
+ if(!is_living_thread(thread))
198
+ {
199
+ return ST_DELETE;
200
+ }
201
+ return ST_CONTINUE;
202
+ }
203
+
204
+ static void
205
+ check_thread_contexts(void)
206
+ {
207
+ threads_table_t *threads_table;
208
+
209
+ Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
210
+ st_foreach(threads_table->tbl, threads_table_check_i, 0);
211
+ }
212
+
213
+ /*
214
+ * call-seq:
215
+ * Debugger.started? -> bool
216
+ *
217
+ * Returns +true+ the debugger is started.
218
+ */
219
+ static VALUE
220
+ debug_is_started(VALUE self)
221
+ {
222
+ return IS_STARTED ? Qtrue : Qfalse;
223
+ }
224
+
225
+ static void
226
+ debug_context_mark(void *data)
227
+ {
228
+ debug_context_t *debug_context = (debug_context_t *)data;
229
+ rb_gc_mark(debug_context->breakpoint);
230
+ rb_gc_mark(debug_context->inspected_frame);
231
+ }
232
+
233
+ static void
234
+ debug_context_free(void *data)
235
+ {
236
+ }
237
+
238
+ static int
239
+ exact_stack_size(VALUE thread)
240
+ {
241
+ VALUE locs = rb_funcall(thread, rb_intern("backtrace_locations"), 1, INT2FIX(1));
242
+ int stack_size = (int)RARRAY_LEN(locs);
243
+ if (debug_debugger_stack_size && debug_debugger) {
244
+ fprintf(stderr, "[debug:stacksize] %d\n", stack_size);
245
+ rb_p(locs);
246
+ }
247
+ return stack_size;
248
+ }
249
+
250
+ static VALUE
251
+ debug_context_create(VALUE thread)
252
+ {
253
+ debug_context_t *debug_context;
254
+
255
+ debug_context = ALLOC(debug_context_t);
256
+
257
+ debug_context->thnum = ++thnum_max;
258
+ debug_context->last_file = Qnil;
259
+ debug_context->last_line = Qnil;
260
+ debug_context->flags = 0;
261
+ debug_context->calced_stack_size = exact_stack_size(thread);
262
+
263
+ debug_context->stop_next = -1;
264
+ debug_context->dest_frame = -1;
265
+ debug_context->stop_line = -1;
266
+ debug_context->stop_frame = -1;
267
+
268
+ debug_context->stop_reason = CTX_STOP_NONE;
269
+ debug_context->thread_id = ref2id(thread);
270
+ debug_context->breakpoint = Qnil;
271
+ debug_context->inspected_frame = Qnil;
272
+
273
+ if (rb_obj_class(thread) == cDebugThread) {
274
+ CTX_FL_SET(debug_context, CTX_FL_IGNORE);
275
+ }
276
+ return Data_Wrap_Struct(cContext, debug_context_mark, debug_context_free, debug_context);
277
+ }
278
+
279
+ static VALUE
280
+ debug_context_dup(debug_context_t *debug_context, VALUE self)
281
+ {
282
+ debug_context_t *new_debug_context;
283
+
284
+ new_debug_context = ALLOC(debug_context_t);
285
+ memcpy(new_debug_context, debug_context, sizeof(debug_context_t));
286
+ new_debug_context->stop_next = -1;
287
+ new_debug_context->dest_frame = -1;
288
+ new_debug_context->stop_line = -1;
289
+ new_debug_context->stop_frame = -1;
290
+ new_debug_context->breakpoint = Qnil;
291
+ new_debug_context->inspected_frame = debug_context->inspected_frame;
292
+ CTX_FL_SET(new_debug_context, CTX_FL_DEAD);
293
+
294
+ return Data_Wrap_Struct(cContext, debug_context_mark, debug_context_free, new_debug_context);
295
+ }
296
+
297
+ static void
298
+ thread_context_lookup(VALUE thread, VALUE *context, debug_context_t **debug_context, int create)
299
+ {
300
+ threads_table_t *threads_table;
301
+ VALUE thread_id;
302
+ debug_context_t *l_debug_context;
303
+
304
+ debug_check_started();
305
+
306
+ if (last_thread == thread && last_context != Qnil) {
307
+ *context = last_context;
308
+ if (debug_context) {
309
+ *debug_context = last_debug_context;
310
+ }
311
+ return;
312
+ }
313
+ thread_id = ref2id(thread);
314
+ Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
315
+ if (!st_lookup(threads_table->tbl, thread_id, context) || !*context) {
316
+ if (create) {
317
+ *context = debug_context_create(thread);
318
+ st_insert(threads_table->tbl, thread_id, *context);
319
+ }
320
+ else {
321
+ *context = 0;
322
+ if (debug_context) {
323
+ *debug_context = NULL;
324
+ }
325
+ return;
326
+ }
327
+ }
328
+
329
+ Data_Get_Struct(*context, debug_context_t, l_debug_context);
330
+ if (debug_context) {
331
+ *debug_context = l_debug_context;
332
+ }
333
+
334
+ last_thread = thread;
335
+ last_context = *context;
336
+ last_debug_context = l_debug_context;
337
+ }
338
+
339
+ static VALUE
340
+ dc_inspected_frame(const debug_context_t *debug_context)
341
+ {
342
+ if (NIL_P(debug_context->inspected_frame)) {
343
+ rb_raise(rb_eRuntimeError, "Inspected frame information is not available");
344
+ }
345
+
346
+ return debug_context->inspected_frame;
347
+ }
348
+
349
+ static VALUE
350
+ dc_inspected_frame_get(const debug_context_t *debug_context, int frame_index, enum inspected_frame_type type)
351
+ {
352
+ VALUE frame = rb_ary_entry(dc_inspected_frame(debug_context), frame_index);
353
+ return rb_ary_entry(frame, type);
354
+ }
355
+
356
+ static VALUE
357
+ dc_inspected_frame_location(const debug_context_t *debug_context, int frame_index)
358
+ {
359
+ return dc_inspected_frame_get(debug_context, frame_index, INSPECTED_FRAME_LOCATION);
360
+ }
361
+
362
+ static VALUE
363
+ dc_inspected_frame_self(const debug_context_t *debug_context, int frame_index)
364
+ {
365
+ return dc_inspected_frame_get(debug_context, frame_index, INSPECTED_FRAME_SELF);
366
+ }
367
+
368
+ static VALUE
369
+ dc_inspected_frame_class(const debug_context_t *debug_context, int frame_index)
370
+ {
371
+ return dc_inspected_frame_get(debug_context, frame_index, INSPECTED_FRAME_CLASS);
372
+ }
373
+
374
+ static VALUE
375
+ dc_inspected_frame_binding(const debug_context_t *debug_context, int frame_index)
376
+ {
377
+ return dc_inspected_frame_get(debug_context, frame_index, INSPECTED_FRAME_BIDING);
378
+ }
379
+
380
+ #if 0 /* unused */
381
+ static VALUE
382
+ dc_inspected_frame_iseq(const debug_context_t *debug_context, int frame_index)
383
+ {
384
+ return dc_inspected_frame_get(debug_context, frame_index, INSPECTED_FRAME_ISEQ);
385
+ }
386
+ #endif
387
+
388
+ static int
389
+ dc_stack_size(const debug_context_t *debug_context)
390
+ {
391
+ if (!NIL_P(debug_context->inspected_frame)) {
392
+ int stack_size = (int)RARRAY_LEN(debug_context->inspected_frame);
393
+
394
+ /* for debug */
395
+ if (0 && debug_debugger && stack_size != debug_context->calced_stack_size) {
396
+ rb_p(debug_context->inspected_frame);
397
+ rb_bug("dc_stack_size: stack size calculation miss: calced %d but %d",
398
+ debug_context->calced_stack_size, stack_size);
399
+ }
400
+
401
+ return stack_size;
402
+ }
403
+ else {
404
+ /* TOOD: optimize */
405
+ if (0 && debug_debugger) {
406
+ int stack_size = exact_stack_size(rb_thread_current());
407
+ if (stack_size != debug_context->calced_stack_size) {
408
+ rb_bug("dc_stack_size: stack size calculation miss: calced %d but %d",
409
+ debug_context->calced_stack_size, stack_size);
410
+ }
411
+ }
412
+ return debug_context->calced_stack_size;
413
+ }
414
+ }
415
+
416
+ static void
417
+ halt_while_other_thread_is_active(VALUE current_thread, debug_context_t *debug_context)
418
+ {
419
+ while(1) {
420
+ /* halt execution of the current thread if the debugger
421
+ is activated in another
422
+ */
423
+ while(locker != Qnil && locker != current_thread) {
424
+ add_to_locked(current_thread);
425
+ rb_thread_stop();
426
+ }
427
+
428
+ /* stop the current thread if it's marked as suspended */
429
+ if(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND) && locker != current_thread) {
430
+ CTX_FL_SET(debug_context, CTX_FL_WAS_RUNNING);
431
+ rb_thread_stop();
432
+ }
433
+ else break;
434
+ }
435
+ }
436
+
437
+ static void
438
+ trace_cleanup(debug_context_t *debug_context)
439
+ {
440
+ VALUE next_thread;
441
+ debug_context->stop_reason = CTX_STOP_NONE;
442
+
443
+ /* check that all contexts point to alive threads */
444
+ if (hook_count - last_check > 3000) {
445
+ check_thread_contexts();
446
+ last_check = hook_count;
447
+ }
448
+
449
+ /* release a lock */
450
+ locker = Qnil;
451
+
452
+ /* let the next thread to run */
453
+ next_thread = remove_from_locked();
454
+ if(next_thread != Qnil) {
455
+ rb_thread_run(next_thread);
456
+ }
457
+ }
458
+
459
+ static void
460
+ trace_debug_print(rb_trace_arg_t *trace_arg, debug_context_t *debug_context)
461
+ {
462
+ if (debug_debugger == Qtrue) {
463
+ VALUE path = rb_tracearg_path(trace_arg);
464
+ VALUE line = rb_tracearg_lineno(trace_arg);
465
+ VALUE event = rb_tracearg_event(trace_arg);
466
+ VALUE mid = rb_tracearg_method_id(trace_arg);
467
+ fprintf(stderr, "%*s[debug:event#%d] %s@%s:%d %s\n",
468
+ debug_context->calced_stack_size, "",
469
+ debug_context->thnum,
470
+ rb_id2name(SYM2ID(event)),
471
+ RSTRING_PTR(path),
472
+ NUM2INT(line),
473
+ NIL_P(mid) ? "" : rb_id2name(SYM2ID(mid))
474
+ );
475
+ }
476
+ }
477
+
478
+ #define TRACE_SETUP \
479
+ rb_trace_arg_t *trace_arg = rb_tracearg_from_tracepoint(tpval); \
480
+ VALUE current_thread = rb_thread_current(); \
481
+ debug_context_t *debug_context; \
482
+ VALUE context; \
483
+ thread_context_lookup(current_thread, &context, &debug_context, 1); \
484
+ trace_debug_print(trace_arg, debug_context);
485
+
486
+ #define TRACE_COMMON() \
487
+ if (trace_common(trace_arg, debug_context, current_thread) == 0) { return; }
488
+
489
+ static int
490
+ trace_common(rb_trace_arg_t *trace_arg, debug_context_t *debug_context, VALUE current_thread)
491
+ {
492
+ hook_count++;
493
+
494
+ if (CTX_FL_TEST(debug_context, CTX_FL_IGNORE)) return 0;
495
+
496
+ halt_while_other_thread_is_active(current_thread, debug_context);
497
+
498
+ if (locker != Qnil) return 0;
499
+
500
+ locker = current_thread;
501
+
502
+ /* ignore a skipped section of code */
503
+ if (CTX_FL_TEST(debug_context, CTX_FL_SKIPPED)) {
504
+ trace_cleanup(debug_context);
505
+ return 0;
506
+ }
507
+
508
+ /* Sometimes duplicate RUBY_EVENT_LINE messages get generated by the compiler.
509
+ * Ignore them. */
510
+ if (0 /* TODO: check was emitted */) {
511
+ trace_cleanup(debug_context);
512
+ return 0;
513
+ }
514
+
515
+ /* There can be many event calls per line, but we only want *one* breakpoint per line. */
516
+ if (debug_context->last_line != rb_tracearg_lineno(trace_arg) ||
517
+ debug_context->last_file != rb_tracearg_path(trace_arg)) {
518
+ CTX_FL_SET(debug_context, CTX_FL_ENABLE_BKPT);
519
+ }
520
+
521
+ return 1;
522
+ }
523
+
524
+ struct call_with_inspection_data {
525
+ debug_context_t *debug_context;
526
+ VALUE context;
527
+ ID id;
528
+ int argc;
529
+ VALUE *argv;
530
+ };
531
+
532
+ static VALUE
533
+ open_debug_inspector_i(const rb_debug_inspector_t *inspector, void *data)
534
+ {
535
+ struct call_with_inspection_data *cwi = (struct call_with_inspection_data *)data;
536
+ VALUE inspected_frame = rb_ary_new();
537
+ VALUE locs = rb_debug_inspector_backtrace_locations(inspector);
538
+ int i;
539
+
540
+ for (i=0; i<RARRAY_LEN(locs); i++) {
541
+ VALUE frame = rb_ary_new();
542
+ rb_ary_push(frame, rb_ary_entry(locs, i));
543
+ rb_ary_push(frame, rb_debug_inspector_frame_self_get(inspector, i));
544
+ rb_ary_push(frame, rb_debug_inspector_frame_class_get(inspector, i));
545
+ rb_ary_push(frame, rb_debug_inspector_frame_binding_get(inspector, i));
546
+ rb_ary_push(frame, rb_debug_inspector_frame_iseq_get(inspector, i));
547
+
548
+ rb_ary_push(inspected_frame, frame);
549
+ }
550
+
551
+ cwi->debug_context->inspected_frame = inspected_frame;
552
+ return rb_funcall2(cwi->context, cwi->id, cwi->argc, cwi->argv);
553
+ }
554
+
555
+ static VALUE
556
+ open_debug_inspector(struct call_with_inspection_data *cwi)
557
+ {
558
+ return rb_debug_inspector_open(open_debug_inspector_i, cwi);
559
+ }
560
+
561
+ static VALUE
562
+ close_debug_inspector(struct call_with_inspection_data *cwi)
563
+ {
564
+ cwi->debug_context->inspected_frame = Qnil;
565
+ return Qnil;
566
+ }
567
+
568
+ static VALUE
569
+ call_with_debug_inspector(struct call_with_inspection_data *data)
570
+ {
571
+ return rb_ensure(open_debug_inspector, (VALUE)data, close_debug_inspector, (VALUE)data);
572
+ }
573
+
574
+ static void
575
+ save_current_position(debug_context_t *debug_context, VALUE file, VALUE line)
576
+ {
577
+ debug_context->last_file = file;
578
+ debug_context->last_line = line;
579
+ CTX_FL_UNSET(debug_context, CTX_FL_ENABLE_BKPT);
580
+ CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
581
+ CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
582
+ }
583
+
584
+ static VALUE
585
+ call_at(VALUE context, debug_context_t *debug_context, ID mid, int argc, VALUE a0, VALUE a1)
586
+ {
587
+ struct call_with_inspection_data cwi;
588
+ VALUE argv[2];
589
+
590
+ argv[0] = a0;
591
+ argv[1] = a1;
592
+
593
+ if (0) fprintf(stderr, "call_at: %s\n", rb_id2name(mid));
594
+
595
+ cwi.debug_context = debug_context;
596
+ cwi.context = context;
597
+ cwi.id = mid;
598
+ cwi.argc = argc;
599
+ cwi.argv = &argv[0];
600
+ return call_with_debug_inspector(&cwi);
601
+ }
602
+
603
+ static VALUE
604
+ call_at_line(VALUE context, debug_context_t *debug_context, VALUE file, VALUE line)
605
+ {
606
+ save_current_position(debug_context, file, line);
607
+ return call_at(context, debug_context, rb_intern("at_line"), 2, file, line);
608
+ }
609
+
610
+ static VALUE
611
+ call_at_tracing(VALUE context, debug_context_t *debug_context, VALUE file, VALUE line)
612
+ {
613
+ return call_at(context, debug_context, rb_intern("at_tracing"), 2, file, line);
614
+ }
615
+
616
+ static VALUE
617
+ call_at_breakpoint(VALUE context, debug_context_t *debug_context, VALUE breakpoint)
618
+ {
619
+ debug_context->stop_reason = CTX_STOP_BREAKPOINT;
620
+ return call_at(context, debug_context, rb_intern("at_breakpoint"), 1, breakpoint, 0);
621
+ }
622
+
623
+ static VALUE
624
+ call_at_catchpoint(VALUE context, debug_context_t *debug_context, VALUE exp)
625
+ {
626
+ return call_at(context, debug_context, rb_intern("at_catchpoint"), 1, exp, 0);
627
+ }
628
+
629
+ static void
630
+ call_at_line_check(VALUE binding, debug_context_t *debug_context, VALUE breakpoint, VALUE context, VALUE file, VALUE line)
631
+ {
632
+ debug_context->stop_reason = CTX_STOP_STEP;
633
+
634
+ /* check breakpoint expression */
635
+ if (breakpoint != Qnil) {
636
+ if (!check_breakpoint_expression(breakpoint, binding)) return;// TODO
637
+ if (!check_breakpoint_hit_condition(breakpoint)) return;// TODO
638
+
639
+ if (breakpoint != debug_context->breakpoint) {
640
+ call_at_breakpoint(context, debug_context, breakpoint);
641
+ }
642
+ else {
643
+ debug_context->breakpoint = Qnil;
644
+ }
645
+ }
646
+
647
+ reset_stepping_stop_points(debug_context);
648
+ call_at_line(context, debug_context, file, line);
649
+ }
650
+
651
+ static void
652
+ line_tracepoint(VALUE tpval, void *data)
653
+ {
654
+ int moved = 0; /* TODO */
655
+ VALUE breakpoint = Qnil;
656
+ VALUE file, line;
657
+ TRACE_SETUP;
658
+
659
+ TRACE_COMMON();
660
+
661
+ CTX_FL_SET(debug_context, CTX_FL_STEPPED);
662
+
663
+ file = rb_tracearg_path(trace_arg);
664
+ line = rb_tracearg_lineno(trace_arg);
665
+
666
+ if(RTEST(tracing) || CTX_FL_TEST(debug_context, CTX_FL_TRACING)) {
667
+ call_at_tracing(context, debug_context, file, line);
668
+ }
669
+
670
+ if (debug_context->dest_frame == -1 ||
671
+ dc_stack_size(debug_context) == debug_context->dest_frame) {
672
+ if (moved || !CTX_FL_TEST(debug_context, CTX_FL_FORCE_MOVE)) debug_context->stop_next--;
673
+ if (debug_context->stop_next < 0) debug_context->stop_next = -1;
674
+
675
+ if (moved || (CTX_FL_TEST(debug_context, CTX_FL_STEPPED) &&
676
+ !CTX_FL_TEST(debug_context, CTX_FL_FORCE_MOVE))) {
677
+ debug_context->stop_line--;
678
+ CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
679
+ }
680
+ }
681
+ else if (dc_stack_size(debug_context) < debug_context->dest_frame) {
682
+ debug_context->stop_next = 0;
683
+ }
684
+
685
+ if (0) fprintf(stderr, "stop_next: %d, stop_line: %d\n",
686
+ debug_context->stop_next,
687
+ debug_context->stop_line
688
+ );
689
+
690
+ if (debug_context->stop_next == 0 ||
691
+ debug_context->stop_line == 0 ||
692
+ (breakpoint = check_breakpoints_by_pos(debug_context, file, line)) != Qnil) {
693
+ call_at_line_check(rb_tracearg_binding(trace_arg), debug_context, breakpoint, context, file, line);
694
+ }
695
+
696
+ trace_cleanup(debug_context);
697
+ }
698
+
699
+ static void
700
+ call_tracepoint(VALUE tpval, void *data)
701
+ {
702
+ VALUE breakpoint;
703
+ VALUE klass, mid, self;
704
+ TRACE_SETUP;
705
+
706
+ debug_context->calced_stack_size ++;
707
+ if (debug_debugger_stack_size && debug_debugger) {
708
+ fprintf(stderr, "[debug:stacksize] %d (add)\n", debug_context->calced_stack_size);
709
+ }
710
+
711
+ TRACE_COMMON();
712
+
713
+ klass = rb_tracearg_defined_class(trace_arg);
714
+ mid = rb_tracearg_method_id(trace_arg);
715
+ self = rb_tracearg_self(trace_arg);
716
+
717
+ breakpoint = check_breakpoints_by_method(debug_context, klass, mid, self);
718
+
719
+ if (breakpoint != Qnil) {
720
+ VALUE binding = rb_tracearg_binding(trace_arg);
721
+
722
+ if (!check_breakpoint_expression(breakpoint, binding)) return;
723
+ if (!check_breakpoint_hit_condition(breakpoint)) return;
724
+ if (breakpoint != debug_context->breakpoint) {
725
+ call_at_breakpoint(context, debug_context, breakpoint);
726
+ }
727
+ else {
728
+ debug_context->breakpoint = Qnil;
729
+ }
730
+ call_at_line(context, debug_context, rb_tracearg_path(trace_arg), rb_tracearg_lineno(trace_arg));
731
+ }
732
+
733
+ trace_cleanup(debug_context);
734
+ }
735
+
736
+ static void
737
+ return_tracepoint(VALUE tpval, void *data)
738
+ {
739
+ TRACE_SETUP;
740
+
741
+ debug_context->calced_stack_size --;
742
+
743
+ TRACE_COMMON();
744
+
745
+ if (debug_context->calced_stack_size + 1 == debug_context->stop_frame) {
746
+ debug_context->stop_next = 1;
747
+ debug_context->stop_frame = 0;
748
+ /* NOTE: can't use call_at_line function here to trigger a debugger event.
749
+ this can lead to segfault. We should only unroll the stack on this event.
750
+ */
751
+ }
752
+
753
+ CTX_FL_SET(debug_context, CTX_FL_ENABLE_BKPT);
754
+
755
+ trace_cleanup(debug_context);
756
+ }
757
+
758
+
759
+ static void
760
+ misc_call_tracepoint(VALUE tpval, void *data)
761
+ {
762
+ TRACE_SETUP;
763
+ debug_context->calced_stack_size ++;
764
+ if (debug_debugger_stack_size && debug_debugger) {
765
+ fprintf(stderr, "[debug:stacksize] %d (add)\n", debug_context->calced_stack_size);
766
+ }
767
+ }
768
+
769
+ static void
770
+ misc_return_tracepoint(VALUE tpval, void *data)
771
+ {
772
+ TRACE_SETUP;
773
+
774
+ debug_context->calced_stack_size --;
775
+
776
+ if (debug_debugger_stack_size && debug_debugger) {
777
+ fprintf(stderr, "[debug:stacksize] %d (dec)\n", debug_context->calced_stack_size);
778
+ }
779
+ CTX_FL_SET(debug_context, CTX_FL_ENABLE_BKPT);
780
+ }
781
+
782
+ static void
783
+ raise_tracepoint(VALUE tpval, void *data)
784
+ {
785
+ VALUE ancestors;
786
+ VALUE expn_class, aclass;
787
+ VALUE binding;
788
+ VALUE err = rb_errinfo();
789
+ int i;
790
+ TRACE_SETUP;
791
+
792
+ TRACE_COMMON();
793
+
794
+ if (post_mortem == Qtrue) {
795
+ binding = rb_tracearg_binding(trace_arg);
796
+ rb_ivar_set(rb_errinfo(), rb_intern("@__debug_file"), rb_tracearg_path(trace_arg));
797
+ rb_ivar_set(rb_errinfo(), rb_intern("@__debug_line"), rb_tracearg_lineno(trace_arg));
798
+ rb_ivar_set(rb_errinfo(), rb_intern("@__debug_binding"), rb_tracearg_binding(trace_arg));
799
+ rb_ivar_set(rb_errinfo(), rb_intern("@__debug_context"), debug_context_dup(debug_context, rb_tracearg_self(trace_arg)));
800
+ }
801
+
802
+ expn_class = rb_obj_class(err);
803
+
804
+ if (rdebug_catchpoints == Qnil ||
805
+ (dc_stack_size(debug_context) == 0) ||
806
+ CTX_FL_TEST(debug_context, CTX_FL_CATCHING) ||
807
+ (RHASH_TBL(rdebug_catchpoints)->num_entries) == 0) {
808
+ goto cleanup;
809
+ }
810
+
811
+ ancestors = rb_mod_ancestors(expn_class);
812
+
813
+ for (i = 0; i < RARRAY_LEN(ancestors); i++) {
814
+ VALUE mod_name;
815
+ VALUE hit_count;
816
+
817
+ aclass = rb_ary_entry(ancestors, i);
818
+ mod_name = rb_mod_name(aclass);
819
+ hit_count = rb_hash_aref(rdebug_catchpoints, mod_name);
820
+
821
+ if (hit_count != Qnil) {
822
+ /* increment exception */
823
+ rb_hash_aset(rdebug_catchpoints, mod_name, INT2FIX(FIX2INT(hit_count) + 1));
824
+ call_at_catchpoint(context, debug_context, err);
825
+ break;
826
+ }
827
+ }
828
+
829
+ cleanup:
830
+ trace_cleanup(debug_context);
831
+ }
832
+
833
+
834
+ static void
835
+ register_debug_trace_points(void)
836
+ {
837
+ int i;
838
+ VALUE traces = tracepoints;
839
+
840
+ if (NIL_P(traces)) {
841
+ traces = rb_ary_new();
842
+
843
+ rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_LINE, line_tracepoint, 0));
844
+ rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_CALL, call_tracepoint, 0));
845
+ rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_RAISE, raise_tracepoint, 0));
846
+ rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_RETURN | RUBY_EVENT_END, return_tracepoint, 0));
847
+ rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_C_CALL | RUBY_EVENT_B_CALL, misc_call_tracepoint, 0));
848
+ rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_C_RETURN | RUBY_EVENT_B_RETURN, misc_return_tracepoint, 0));
849
+ tracepoints = traces;
850
+ }
851
+
852
+ for (i=0; i<RARRAY_LEN(traces); i++) {
853
+ rb_tracepoint_enable(rb_ary_entry(traces, i));
854
+ }
855
+ }
856
+
857
+ static void
858
+ clear_debug_trace_points(void)
859
+ {
860
+ int i;
861
+
862
+ for (i=0; i<RARRAY_LEN(tracepoints); i++) {
863
+ rb_tracepoint_disable(rb_ary_entry(tracepoints, i));
864
+ }
865
+ }
866
+
867
+ static VALUE
868
+ debug_stop_i(VALUE self)
869
+ {
870
+ if (IS_STARTED) {
871
+ debug_stop(self);
872
+ }
873
+ return Qnil;
874
+ }
875
+
876
+ /*
877
+ * call-seq:
878
+ * Debugger.start_ -> bool
879
+ * Debugger.start_ { ... } -> bool
880
+ *
881
+ * This method is internal and activates the debugger. Use
882
+ * Debugger.start (from <tt>lib/ruby-debug-base.rb</tt>) instead.
883
+ *
884
+ * The return value is the value of !Debugger.started? <i>before</i>
885
+ * issuing the +start+; That is, +true+ is returned, unless debugger
886
+ * was previously started.
887
+
888
+ * If a block is given, it starts debugger and yields to block. When
889
+ * the block is finished executing it stops the debugger with
890
+ * Debugger.stop method. Inside the block you will probably want to
891
+ * have a call to Debugger.debugger. For example:
892
+ * Debugger.start{debugger; foo} # Stop inside of foo
893
+ *
894
+ * Also, ruby-debug only allows
895
+ * one invocation of debugger at a time; nested Debugger.start's
896
+ * have no effect and you can't use this inside the debugger itself.
897
+ *
898
+ * <i>Note that if you want to completely remove the debugger hook,
899
+ * you must call Debugger.stop as many times as you called
900
+ * Debugger.start method.</i>
901
+ */
902
+ static VALUE
903
+ debug_start(VALUE self)
904
+ {
905
+ VALUE result;
906
+ start_count++;
907
+
908
+ if(IS_STARTED) {
909
+ result = Qfalse;
910
+ }
911
+ else {
912
+ locker = Qnil;
913
+ rdebug_breakpoints = rb_ary_new();
914
+ rdebug_catchpoints = rb_hash_new();
915
+ rdebug_threads_tbl = threads_table_create();
916
+
917
+ register_debug_trace_points();
918
+ debug_context_create(rb_thread_current());
919
+ result = Qtrue;
920
+ }
921
+
922
+ if(rb_block_given_p())
923
+ rb_ensure(rb_yield, self, debug_stop_i, self);
924
+
925
+ return result;
926
+ }
927
+
928
+ /*
929
+ * call-seq:
930
+ * Debugger.stop -> bool
931
+ *
932
+ * This method disables the debugger. It returns +true+ if the debugger is disabled,
933
+ * otherwise it returns +false+.
934
+ *
935
+ * <i>Note that if you want to complete remove the debugger hook,
936
+ * you must call Debugger.stop as many times as you called
937
+ * Debugger.start method.</i>
938
+ */
939
+ static VALUE
940
+ debug_stop(VALUE self)
941
+ {
942
+ debug_check_started();
943
+
944
+ start_count--;
945
+ if(start_count)
946
+ return Qfalse;
947
+
948
+
949
+ clear_debug_trace_points();
950
+
951
+ locker = Qnil;
952
+ rdebug_breakpoints = Qnil;
953
+ rdebug_threads_tbl = Qnil;
954
+
955
+ return Qtrue;
956
+ }
957
+
958
+ static int
959
+ find_last_context_func(st_data_t key, st_data_t value, st_data_t *result_arg)
960
+ {
961
+ debug_context_t *debug_context;
962
+ VALUE *result = (VALUE*)result_arg;
963
+ if(!value)
964
+ return ST_CONTINUE;
965
+
966
+ Data_Get_Struct((VALUE)value, debug_context_t, debug_context);
967
+ if(debug_context->thnum == last_debugged_thnum)
968
+ {
969
+ *result = value;
970
+ return ST_STOP;
971
+ }
972
+ return ST_CONTINUE;
973
+ }
974
+
975
+ /*
976
+ * call-seq:
977
+ * Debugger.last_interrupted -> context
978
+ *
979
+ * Returns last debugged context.
980
+ */
981
+ static VALUE
982
+ debug_last_interrupted(VALUE self)
983
+ {
984
+ VALUE result = Qnil;
985
+ threads_table_t *threads_table;
986
+
987
+ debug_check_started();
988
+
989
+ Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
990
+
991
+ st_foreach(threads_table->tbl, find_last_context_func, (st_data_t)&result);
992
+ return result;
993
+ }
994
+
995
+ /*
996
+ * call-seq:
997
+ * Debugger.current_context -> context
998
+ *
999
+ * Returns current context.
1000
+ * <i>Note:</i> Debugger.current_context.thread == Thread.current
1001
+ */
1002
+ static VALUE
1003
+ debug_current_context(VALUE self)
1004
+ {
1005
+ VALUE thread, context;
1006
+
1007
+ debug_check_started();
1008
+
1009
+ thread = rb_thread_current();
1010
+ thread_context_lookup(thread, &context, NULL, 1);
1011
+
1012
+ return context;
1013
+ }
1014
+
1015
+ /*
1016
+ * call-seq:
1017
+ * Debugger.thread_context(thread) -> context
1018
+ *
1019
+ * Returns context of the thread passed as an argument.
1020
+ */
1021
+ static VALUE
1022
+ debug_thread_context(VALUE self, VALUE thread)
1023
+ {
1024
+ VALUE context;
1025
+
1026
+ debug_check_started();
1027
+ thread_context_lookup(thread, &context, NULL, 1);
1028
+ return context;
1029
+ }
1030
+
1031
+ /*
1032
+ * call-seq:
1033
+ * Debugger.contexts -> array
1034
+ *
1035
+ * Returns an array of all contexts.
1036
+ */
1037
+ static VALUE
1038
+ debug_contexts(VALUE self)
1039
+ {
1040
+ volatile VALUE list;
1041
+ volatile VALUE new_list;
1042
+ VALUE thread, context;
1043
+ threads_table_t *threads_table;
1044
+ debug_context_t *debug_context;
1045
+ int i;
1046
+
1047
+ debug_check_started();
1048
+
1049
+ new_list = rb_ary_new();
1050
+ list = rb_funcall(rb_cThread, rb_intern("list"), 0);
1051
+
1052
+ for (i = 0; i < RARRAY_LEN(list); i++) {
1053
+ thread = rb_ary_entry(list, i);
1054
+ thread_context_lookup(thread, &context, NULL, 1);
1055
+ rb_ary_push(new_list, context);
1056
+ }
1057
+
1058
+ threads_table_clear(rdebug_threads_tbl);
1059
+ Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
1060
+
1061
+ for (i = 0; i < RARRAY_LEN(new_list); i++) {
1062
+ context = rb_ary_entry(new_list, i);
1063
+ Data_Get_Struct(context, debug_context_t, debug_context);
1064
+ st_insert(threads_table->tbl, debug_context->thread_id, context);
1065
+ }
1066
+
1067
+ return new_list;
1068
+ }
1069
+
1070
+ /*
1071
+ * call-seq:
1072
+ * Debugger.suspend -> Debugger
1073
+ *
1074
+ * Suspends all contexts.
1075
+ */
1076
+ static VALUE
1077
+ debug_suspend(VALUE self)
1078
+ {
1079
+ VALUE current, context;
1080
+ VALUE context_list;
1081
+ debug_context_t *debug_context;
1082
+ int i;
1083
+
1084
+ debug_check_started();
1085
+
1086
+ context_list = debug_contexts(self);
1087
+ thread_context_lookup(rb_thread_current(), &current, NULL, 1);
1088
+
1089
+ for(i = 0; i < RARRAY_LEN(context_list); i++)
1090
+ {
1091
+ context = rb_ary_entry(context_list, i);
1092
+ if(current == context)
1093
+ continue;
1094
+ Data_Get_Struct(context, debug_context_t, debug_context);
1095
+ context_suspend_0(debug_context);
1096
+ }
1097
+
1098
+ return self;
1099
+ }
1100
+
1101
+ /*
1102
+ * call-seq:
1103
+ * Debugger.resume -> Debugger
1104
+ *
1105
+ * Resumes all contexts.
1106
+ */
1107
+ static VALUE
1108
+ debug_resume(VALUE self)
1109
+ {
1110
+ VALUE current, context;
1111
+ VALUE context_list;
1112
+ debug_context_t *debug_context;
1113
+ int i;
1114
+
1115
+ debug_check_started();
1116
+
1117
+ context_list = debug_contexts(self);
1118
+
1119
+ thread_context_lookup(rb_thread_current(), &current, NULL, 1);
1120
+ for(i = 0; i < RARRAY_LEN(context_list); i++)
1121
+ {
1122
+ context = rb_ary_entry(context_list, i);
1123
+ if(current == context)
1124
+ continue;
1125
+ Data_Get_Struct(context, debug_context_t, debug_context);
1126
+ context_resume_0(debug_context);
1127
+ }
1128
+
1129
+ rb_thread_schedule();
1130
+
1131
+ return self;
1132
+ }
1133
+
1134
+ /*
1135
+ * call-seq:
1136
+ * Debugger.tracing -> bool
1137
+ *
1138
+ * Returns +true+ if the global tracing is activated.
1139
+ */
1140
+ static VALUE
1141
+ debug_tracing(VALUE self)
1142
+ {
1143
+ return tracing;
1144
+ }
1145
+
1146
+ /*
1147
+ * call-seq:
1148
+ * Debugger.tracing = bool
1149
+ *
1150
+ * Sets the global tracing flag.
1151
+ */
1152
+ static VALUE
1153
+ debug_set_tracing(VALUE self, VALUE value)
1154
+ {
1155
+ tracing = RTEST(value) ? Qtrue : Qfalse;
1156
+ return value;
1157
+ }
1158
+
1159
+ /*
1160
+ * call-seq:
1161
+ * Debugger.post_mortem? -> bool
1162
+ *
1163
+ * Returns +true+ if post-moterm debugging is enabled.
1164
+ */
1165
+ static VALUE
1166
+ debug_post_mortem(VALUE self)
1167
+ {
1168
+ return post_mortem;
1169
+ }
1170
+
1171
+ /*
1172
+ * call-seq:
1173
+ * Debugger.post_mortem = bool
1174
+ *
1175
+ * Sets post-moterm flag.
1176
+ * FOR INTERNAL USE ONLY.
1177
+ */
1178
+ static VALUE
1179
+ debug_set_post_mortem(VALUE self, VALUE value)
1180
+ {
1181
+ debug_check_started();
1182
+
1183
+ post_mortem = RTEST(value) ? Qtrue : Qfalse;
1184
+ return value;
1185
+ }
1186
+
1187
+ /*
1188
+ * call-seq:
1189
+ * Debugger.track_fame_args? -> bool
1190
+ *
1191
+ * Returns +true+ if the debugger track frame argument values on calls.
1192
+ */
1193
+ static VALUE
1194
+ debug_track_frame_args(VALUE self)
1195
+ {
1196
+ return track_frame_args;
1197
+ }
1198
+
1199
+ /*
1200
+ * call-seq:
1201
+ * Debugger.track_frame_args = bool
1202
+ *
1203
+ * Setting to +true+ will make the debugger save argument info on calls.
1204
+ */
1205
+ static VALUE
1206
+ debug_set_track_frame_args(VALUE self, VALUE value)
1207
+ {
1208
+ track_frame_args = RTEST(value) ? Qtrue : Qfalse;
1209
+ return value;
1210
+ }
1211
+
1212
+ /*
1213
+ * call-seq:
1214
+ * Debugger.keep_frame_binding? -> bool
1215
+ *
1216
+ * Returns +true+ if the debugger will collect frame bindings.
1217
+ */
1218
+ static VALUE
1219
+ debug_keep_frame_binding(VALUE self)
1220
+ {
1221
+ return keep_frame_binding;
1222
+ }
1223
+
1224
+ /*
1225
+ * call-seq:
1226
+ * Debugger.keep_frame_binding = bool
1227
+ *
1228
+ * Setting to +true+ will make the debugger create frame bindings.
1229
+ */
1230
+ static VALUE
1231
+ debug_set_keep_frame_binding(VALUE self, VALUE value)
1232
+ {
1233
+ keep_frame_binding = RTEST(value) ? Qtrue : Qfalse;
1234
+ return value;
1235
+ }
1236
+
1237
+ /* :nodoc: */
1238
+ static VALUE
1239
+ debug_debug(VALUE self)
1240
+ {
1241
+ return debug_debugger;
1242
+ }
1243
+
1244
+ /* :nodoc: */
1245
+ static VALUE
1246
+ debug_set_debug(VALUE self, VALUE value)
1247
+ {
1248
+ debug_debugger = RTEST(value) ? Qtrue : Qfalse;
1249
+ return value;
1250
+ }
1251
+
1252
+ /* :nodoc: */
1253
+ static VALUE
1254
+ debug_thread_inherited(VALUE klass)
1255
+ {
1256
+ rb_raise(rb_eRuntimeError, "Can't inherit Debugger::DebugThread class");
1257
+ }
1258
+
1259
+ /*
1260
+ * call-seq:
1261
+ * Debugger.debug_load(file, stop = false, increment_start = false) -> nil
1262
+ *
1263
+ * Same as Kernel#load but resets current context's frames.
1264
+ * +stop+ parameter forces the debugger to stop at the first line of code in the +file+
1265
+ * +increment_start+ determines if start_count should be incremented. When
1266
+ * control threads are used, they have to be set up before loading the
1267
+ * debugger; so here +increment_start+ will be false.
1268
+ * FOR INTERNAL USE ONLY.
1269
+ */
1270
+ static VALUE
1271
+ debug_debug_load(int argc, VALUE *argv, VALUE self)
1272
+ {
1273
+ VALUE file, stop, context, increment_start;
1274
+ debug_context_t *debug_context;
1275
+ int state = 0;
1276
+
1277
+ if (rb_scan_args(argc, argv, "12", &file, &stop, &increment_start) == 1) {
1278
+ stop = Qfalse;
1279
+ increment_start = Qtrue;
1280
+ }
1281
+
1282
+ debug_start(self);
1283
+ if (Qfalse == increment_start) start_count--;
1284
+
1285
+ context = debug_current_context(self);
1286
+ Data_Get_Struct(context, debug_context_t, debug_context);
1287
+
1288
+ /* debug_context->stack_size = 0; */
1289
+
1290
+ if (RTEST(stop)) {
1291
+ debug_context->stop_next = 1;
1292
+ }
1293
+ /* Initializing $0 to the script's path */
1294
+ ruby_script(RSTRING_PTR(file));
1295
+ rb_load_protect(file, 0, &state);
1296
+ if (0 != state)
1297
+ {
1298
+ VALUE errinfo = rb_errinfo();
1299
+ debug_suspend(self);
1300
+ reset_stepping_stop_points(debug_context);
1301
+ rb_set_errinfo(Qnil);
1302
+ return errinfo;
1303
+ }
1304
+
1305
+ /* We should run all at_exit handler's in order to provide,
1306
+ * for instance, a chance to run all defined test cases */
1307
+ rb_exec_end_proc();
1308
+
1309
+ /* We could have issued a Debugger.stop inside the debug
1310
+ session. */
1311
+ if (start_count > 0)
1312
+ debug_stop(self);
1313
+
1314
+ return Qnil;
1315
+ }
1316
+
1317
+ static VALUE
1318
+ set_current_skipped_status(VALUE status)
1319
+ {
1320
+ VALUE context;
1321
+ debug_context_t *debug_context;
1322
+
1323
+ context = debug_current_context(Qnil);
1324
+ Data_Get_Struct(context, debug_context_t, debug_context);
1325
+ if (status) {
1326
+ CTX_FL_SET(debug_context, CTX_FL_SKIPPED);
1327
+ }
1328
+ else {
1329
+ CTX_FL_UNSET(debug_context, CTX_FL_SKIPPED);
1330
+ }
1331
+ return Qnil;
1332
+ }
1333
+
1334
+ /*
1335
+ * call-seq:
1336
+ * Debugger.skip { block } -> obj or nil
1337
+ *
1338
+ * The code inside of the block is escaped from the debugger.
1339
+ */
1340
+ static VALUE
1341
+ debug_skip(VALUE self)
1342
+ {
1343
+ if (!rb_block_given_p()) {
1344
+ rb_raise(rb_eArgError, "called without a block");
1345
+ }
1346
+
1347
+ if (!IS_STARTED) {
1348
+ return rb_yield(Qnil);
1349
+ }
1350
+ set_current_skipped_status(Qtrue);
1351
+ return rb_ensure(rb_yield, Qnil, set_current_skipped_status, Qfalse);
1352
+ }
1353
+
1354
+ static VALUE
1355
+ debug_at_exit_c(VALUE proc)
1356
+ {
1357
+ return rb_funcall(proc, rb_intern("call"), 0);
1358
+ }
1359
+
1360
+ static void
1361
+ debug_at_exit_i(VALUE proc)
1362
+ {
1363
+ if(!IS_STARTED)
1364
+ {
1365
+ debug_at_exit_c(proc);
1366
+ }
1367
+ else
1368
+ {
1369
+ set_current_skipped_status(Qtrue);
1370
+ rb_ensure(debug_at_exit_c, proc, set_current_skipped_status, Qfalse);
1371
+ }
1372
+ }
1373
+
1374
+ /*
1375
+ * call-seq:
1376
+ * Debugger.debug_at_exit { block } -> proc
1377
+ *
1378
+ * Register <tt>at_exit</tt> hook which is escaped from the debugger.
1379
+ * FOR INTERNAL USE ONLY.
1380
+ */
1381
+ static VALUE
1382
+ debug_at_exit(VALUE self)
1383
+ {
1384
+ VALUE proc;
1385
+ if (!rb_block_given_p())
1386
+ rb_raise(rb_eArgError, "called without a block");
1387
+ proc = rb_block_proc();
1388
+ rb_set_end_proc(debug_at_exit_i, proc);
1389
+ return proc;
1390
+ }
1391
+
1392
+ /*
1393
+ * call-seq:
1394
+ * context.step(steps, force = false)
1395
+ *
1396
+ * Stops the current context after a number of +steps+ are made.
1397
+ * +force+ parameter (if true) ensures that the cursor moves from the current line.
1398
+ */
1399
+ static VALUE
1400
+ context_stop_next(int argc, VALUE *argv, VALUE self)
1401
+ {
1402
+ VALUE steps, force;
1403
+ debug_context_t *debug_context;
1404
+
1405
+ debug_check_started();
1406
+
1407
+ rb_scan_args(argc, argv, "11", &steps, &force);
1408
+ if (FIX2INT(steps) < 0) {
1409
+ rb_raise(rb_eRuntimeError, "Steps argument can't be negative.");
1410
+ }
1411
+
1412
+ Data_Get_Struct(self, debug_context_t, debug_context);
1413
+
1414
+ debug_context->stop_next = FIX2INT(steps);
1415
+
1416
+ if (RTEST(force)) {
1417
+ CTX_FL_SET(debug_context, CTX_FL_FORCE_MOVE);
1418
+ }
1419
+ else {
1420
+ CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
1421
+ }
1422
+
1423
+ return steps;
1424
+ }
1425
+
1426
+ /*
1427
+ * call-seq:
1428
+ * context.step_over(steps, frame = nil, force = false)
1429
+ *
1430
+ * Steps over a +steps+ number of times.
1431
+ * Make step over operation on +frame+, by default the current frame.
1432
+ * +force+ parameter (if true) ensures that the cursor moves from the current line.
1433
+ */
1434
+ static VALUE
1435
+ context_step_over(int argc, VALUE *argv, VALUE self)
1436
+ {
1437
+ VALUE lines, frame, force;
1438
+ debug_context_t *debug_context;
1439
+
1440
+ debug_check_started();
1441
+ Data_Get_Struct(self, debug_context_t, debug_context);
1442
+
1443
+ if (dc_stack_size(debug_context) == 0) {
1444
+ rb_raise(rb_eRuntimeError, "No frames collected.");
1445
+ }
1446
+
1447
+ rb_scan_args(argc, argv, "12", &lines, &frame, &force);
1448
+ debug_context->stop_line = FIX2INT(lines);
1449
+ CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
1450
+
1451
+ if (frame == Qnil) {
1452
+ debug_context->dest_frame = dc_stack_size(debug_context);
1453
+ }
1454
+ else {
1455
+ if (FIX2INT(frame) < 0 && FIX2INT(frame) >= dc_stack_size(debug_context)) {
1456
+ rb_raise(rb_eRuntimeError, "Destination frame is out of range.");
1457
+ debug_context->dest_frame = dc_stack_size(debug_context) - FIX2INT(frame);
1458
+ }
1459
+ }
1460
+
1461
+ if (RTEST(force)) {
1462
+ CTX_FL_SET(debug_context, CTX_FL_FORCE_MOVE);
1463
+ }
1464
+ else {
1465
+ CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
1466
+ }
1467
+
1468
+ return Qnil;
1469
+ }
1470
+
1471
+ /*
1472
+ * call-seq:
1473
+ * context.stop_frame(frame)
1474
+ *
1475
+ * Stops when a frame with number +frame+ is activated. Implements +finish+ and +next+ commands.
1476
+ */
1477
+ static VALUE
1478
+ context_stop_frame(VALUE self, VALUE frame)
1479
+ {
1480
+ debug_context_t *debug_context;
1481
+
1482
+ debug_check_started();
1483
+ Data_Get_Struct(self, debug_context_t, debug_context);
1484
+
1485
+ if (FIX2INT(frame) < 0 || FIX2INT(frame) >= dc_stack_size(debug_context)) {
1486
+ rb_raise(rb_eRuntimeError, "Stop frame is out of range.");
1487
+ }
1488
+ debug_context->stop_frame = dc_stack_size(debug_context) - FIX2INT(frame);
1489
+
1490
+ return frame;
1491
+ }
1492
+
1493
+ static int
1494
+ check_frame_number(debug_context_t *debug_context, int frame)
1495
+ {
1496
+ if (frame < 0 || frame >= dc_stack_size(debug_context)) {
1497
+ rb_raise(rb_eArgError, "Invalid frame number %d, stack (0...%d)",
1498
+ frame, dc_stack_size(debug_context) - 1);
1499
+ }
1500
+
1501
+ return frame;
1502
+ }
1503
+
1504
+ static int
1505
+ optional_frame_position(int argc, VALUE *argv)
1506
+ {
1507
+ VALUE level = INT2FIX(0);
1508
+
1509
+ if ((argc > 1) || (argc < 0)) {
1510
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 or 1)", argc);
1511
+ }
1512
+ rb_scan_args(argc, argv, "01", &level);
1513
+ return FIX2INT(level);
1514
+ }
1515
+
1516
+ /*
1517
+ * call-seq:
1518
+ * context.frame_args_info(frame_position=0) -> list
1519
+ if track_frame_args or nil otherwise
1520
+ *
1521
+ * Returns info saved about call arguments (if any saved).
1522
+ */
1523
+ static VALUE
1524
+ context_frame_args_info(int argc, VALUE *argv, VALUE self)
1525
+ {
1526
+ int frame;
1527
+ debug_context_t *debug_context;
1528
+ VALUE binding;
1529
+ const char src[] = "method(__method__).parameters";
1530
+
1531
+ debug_check_started();
1532
+ Data_Get_Struct(self, debug_context_t, debug_context);
1533
+ frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
1534
+
1535
+ binding = dc_inspected_frame_binding(debug_context, frame);
1536
+ return NIL_P(binding) ? rb_ary_new() : rb_funcall(binding, rb_intern("eval"), 1, rb_str_new2(src));
1537
+ }
1538
+
1539
+ /*
1540
+ * call-seq:
1541
+ * context.frame_binding(frame_position=0) -> binding
1542
+ *
1543
+ * Returns frame's binding.
1544
+ */
1545
+ static VALUE
1546
+ context_frame_binding(int argc, VALUE *argv, VALUE self)
1547
+ {
1548
+ int frame;
1549
+ debug_context_t *debug_context;
1550
+
1551
+ debug_check_started();
1552
+ Data_Get_Struct(self, debug_context_t, debug_context);
1553
+ frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
1554
+
1555
+ return dc_inspected_frame_binding(debug_context, frame);
1556
+ }
1557
+
1558
+ /*
1559
+ * call-seq:
1560
+ * context.frame_method(frame_position=0) -> sym
1561
+ *
1562
+ * Returns the sym of the called method.
1563
+ */
1564
+ static VALUE
1565
+ context_frame_id(int argc, VALUE *argv, VALUE self)
1566
+ {
1567
+ int frame;
1568
+ debug_context_t *debug_context;
1569
+ VALUE loc;
1570
+
1571
+ debug_check_started();
1572
+ Data_Get_Struct(self, debug_context_t, debug_context);
1573
+ frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
1574
+ loc = dc_inspected_frame_location(debug_context, frame);
1575
+
1576
+ return rb_str_intern(rb_funcall(loc, rb_intern("label"), 0));
1577
+ }
1578
+
1579
+ /*
1580
+ * call-seq:
1581
+ * context.frame_line(frame_position) -> int
1582
+ *
1583
+ * Returns the line number in the file.
1584
+ */
1585
+ static VALUE
1586
+ context_frame_line(int argc, VALUE *argv, VALUE self)
1587
+ {
1588
+ int frame;
1589
+ debug_context_t *debug_context;
1590
+ VALUE loc;
1591
+
1592
+ debug_check_started();
1593
+ Data_Get_Struct(self, debug_context_t, debug_context);
1594
+ frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
1595
+ loc = dc_inspected_frame_location(debug_context, frame);
1596
+
1597
+ return rb_funcall(loc, rb_intern("lineno"), 0);
1598
+ }
1599
+
1600
+ /*
1601
+ * call-seq:
1602
+ * context.frame_file(frame_position) -> string
1603
+ *
1604
+ * Returns the name of the file.
1605
+ */
1606
+ static VALUE
1607
+ context_frame_file(int argc, VALUE *argv, VALUE self)
1608
+ {
1609
+ int frame;
1610
+ debug_context_t *debug_context;
1611
+ VALUE loc;
1612
+
1613
+ debug_check_started();
1614
+ Data_Get_Struct(self, debug_context_t, debug_context);
1615
+ frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
1616
+ loc = dc_inspected_frame_location(debug_context, frame);
1617
+
1618
+ return rb_funcall(loc, rb_intern("absolute_path"), 0);
1619
+ }
1620
+
1621
+ /*
1622
+ * call-seq:
1623
+ * context.frame_locals(frame) -> hash
1624
+ *
1625
+ * Returns frame's local variables.
1626
+ */
1627
+ static VALUE
1628
+ context_frame_locals(int argc, VALUE *argv, VALUE self)
1629
+ {
1630
+ int frame;
1631
+ debug_context_t *debug_context;
1632
+ VALUE binding;
1633
+ const char src[] = "local_variables.inject({}){|h, v| h[v] = eval(\"#{v}\"); h}";
1634
+
1635
+ debug_check_started();
1636
+ Data_Get_Struct(self, debug_context_t, debug_context);
1637
+ frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
1638
+
1639
+
1640
+ binding = dc_inspected_frame_binding(debug_context, frame);
1641
+ return NIL_P(binding) ? rb_hash_new() : rb_funcall(binding, rb_intern("eval"), 1, rb_str_new2(src));
1642
+ }
1643
+
1644
+ /*
1645
+ * call-seq:
1646
+ * context.frame_args(frame_position=0) -> list
1647
+ *
1648
+ * Returns frame's argument parameters
1649
+ */
1650
+ static VALUE
1651
+ context_frame_args(int argc, VALUE *argv, VALUE self)
1652
+ {
1653
+ int frame;
1654
+ debug_context_t *debug_context;
1655
+ VALUE binding;
1656
+ const char src[] = "__method__ ? self.method(__method__).parameters.map{|(attr, mid)| mid} : []";
1657
+
1658
+ debug_check_started();
1659
+ Data_Get_Struct(self, debug_context_t, debug_context);
1660
+ frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
1661
+
1662
+ binding = dc_inspected_frame_binding(debug_context, frame);
1663
+ return NIL_P(binding) ? rb_ary_new() : rb_funcall(binding, rb_intern("eval"), 1, rb_str_new2(src));
1664
+ }
1665
+
1666
+ /*
1667
+ * call-seq:
1668
+ * context.frame_self(frame_postion=0) -> obj
1669
+ *
1670
+ * Returns self object of the frame.
1671
+ */
1672
+ static VALUE
1673
+ context_frame_self(int argc, VALUE *argv, VALUE self)
1674
+ {
1675
+ int frame;
1676
+ debug_context_t *debug_context;
1677
+
1678
+ debug_check_started();
1679
+ Data_Get_Struct(self, debug_context_t, debug_context);
1680
+ frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
1681
+
1682
+ return dc_inspected_frame_self(debug_context, frame);
1683
+ }
1684
+
1685
+ /*
1686
+ * call-seq:
1687
+ * context.frame_class(frame_position) -> obj
1688
+ *
1689
+ * Returns the real class of the frame.
1690
+ * It could be different than context.frame_self(frame).class
1691
+ */
1692
+ static VALUE
1693
+ context_frame_class(int argc, VALUE *argv, VALUE self)
1694
+ {
1695
+ int frame;
1696
+ debug_context_t *debug_context;
1697
+
1698
+ debug_check_started();
1699
+ Data_Get_Struct(self, debug_context_t, debug_context);
1700
+ frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
1701
+
1702
+ return dc_inspected_frame_class(debug_context, frame);
1703
+ }
1704
+
1705
+ /*
1706
+ * call-seq:
1707
+ * context.stack_size-> int
1708
+ *
1709
+ * Returns the size of the context stack.
1710
+ */
1711
+ static VALUE
1712
+ context_stack_size(VALUE self)
1713
+ {
1714
+ debug_context_t *debug_context;
1715
+
1716
+ debug_check_started();
1717
+ Data_Get_Struct(self, debug_context_t, debug_context);
1718
+
1719
+ return INT2FIX(dc_stack_size(debug_context));
1720
+ }
1721
+
1722
+ /*
1723
+ * call-seq:
1724
+ * context.thread -> thread
1725
+ *
1726
+ * Returns a thread this context is associated with.
1727
+ */
1728
+ static VALUE
1729
+ context_thread(VALUE self)
1730
+ {
1731
+ debug_context_t *debug_context;
1732
+
1733
+ debug_check_started();
1734
+ Data_Get_Struct(self, debug_context_t, debug_context);
1735
+
1736
+ return(id2ref(debug_context->thread_id));
1737
+ }
1738
+
1739
+ /*
1740
+ * call-seq:
1741
+ * context.thnum -> int
1742
+ *
1743
+ * Returns the context's number.
1744
+ */
1745
+ static VALUE
1746
+ context_thnum(VALUE self)
1747
+ {
1748
+ debug_context_t *debug_context;
1749
+
1750
+ debug_check_started();
1751
+ Data_Get_Struct(self, debug_context_t, debug_context);
1752
+
1753
+ return INT2FIX(debug_context->thnum);
1754
+ }
1755
+
1756
+ static void
1757
+ context_suspend_0(debug_context_t *debug_context)
1758
+ {
1759
+ VALUE status;
1760
+
1761
+ status = rb_funcall(context_thread_0(debug_context), rb_intern("status"), 0);
1762
+ if(rb_str_cmp(status, rb_str_new2("run")) == 0) {
1763
+ CTX_FL_SET(debug_context, CTX_FL_WAS_RUNNING);
1764
+ }
1765
+ else if(rb_str_cmp(status, rb_str_new2("sleep")) == 0) {
1766
+ CTX_FL_UNSET(debug_context, CTX_FL_WAS_RUNNING);
1767
+ }
1768
+ else {
1769
+ return;
1770
+ }
1771
+
1772
+ CTX_FL_SET(debug_context, CTX_FL_SUSPEND);
1773
+ }
1774
+
1775
+ static void
1776
+ context_resume_0(debug_context_t *debug_context)
1777
+ {
1778
+ if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND)) {
1779
+ return;
1780
+ }
1781
+
1782
+ CTX_FL_UNSET(debug_context, CTX_FL_SUSPEND);
1783
+
1784
+ if(CTX_FL_TEST(debug_context, CTX_FL_WAS_RUNNING)) {
1785
+ rb_thread_wakeup(context_thread_0(debug_context));
1786
+ }
1787
+ }
1788
+
1789
+ /*
1790
+ * call-seq:
1791
+ * context.suspend -> nil
1792
+ *
1793
+ * Suspends the thread when it is running.
1794
+ */
1795
+ static VALUE
1796
+ context_suspend(VALUE self)
1797
+ {
1798
+ debug_context_t *debug_context;
1799
+
1800
+ debug_check_started();
1801
+
1802
+ Data_Get_Struct(self, debug_context_t, debug_context);
1803
+
1804
+ if(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND)) {
1805
+ rb_raise(rb_eRuntimeError, "Already suspended.");
1806
+ }
1807
+ context_suspend_0(debug_context);
1808
+ return Qnil;
1809
+ }
1810
+
1811
+ /*
1812
+ * call-seq:
1813
+ * context.suspended? -> bool
1814
+ *
1815
+ * Returns +true+ if the thread is suspended by debugger.
1816
+ */
1817
+ static VALUE
1818
+ context_is_suspended(VALUE self)
1819
+ {
1820
+ debug_context_t *debug_context;
1821
+
1822
+ debug_check_started();
1823
+
1824
+ Data_Get_Struct(self, debug_context_t, debug_context);
1825
+ return CTX_FL_TEST(debug_context, CTX_FL_SUSPEND) ? Qtrue : Qfalse;
1826
+ }
1827
+
1828
+ /*
1829
+ * call-seq:
1830
+ * context.resume -> nil
1831
+ *
1832
+ * Resumes the thread from the suspended mode.
1833
+ */
1834
+ static VALUE
1835
+ context_resume(VALUE self)
1836
+ {
1837
+ debug_context_t *debug_context;
1838
+
1839
+ debug_check_started();
1840
+
1841
+ Data_Get_Struct(self, debug_context_t, debug_context);
1842
+ if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND)) {
1843
+ rb_raise(rb_eRuntimeError, "Thread is not suspended.");
1844
+ }
1845
+ context_resume_0(debug_context);
1846
+ return Qnil;
1847
+ }
1848
+
1849
+ /*
1850
+ * call-seq:
1851
+ * context.tracing -> bool
1852
+ *
1853
+ * Returns the tracing flag for the current context.
1854
+ */
1855
+ static VALUE
1856
+ context_tracing(VALUE self)
1857
+ {
1858
+ debug_context_t *debug_context;
1859
+
1860
+ debug_check_started();
1861
+
1862
+ Data_Get_Struct(self, debug_context_t, debug_context);
1863
+ return CTX_FL_TEST(debug_context, CTX_FL_TRACING) ? Qtrue : Qfalse;
1864
+ }
1865
+
1866
+ /*
1867
+ * call-seq:
1868
+ * context.tracing = bool
1869
+ *
1870
+ * Controls the tracing for this context.
1871
+ */
1872
+ static VALUE
1873
+ context_set_tracing(VALUE self, VALUE value)
1874
+ {
1875
+ debug_context_t *debug_context;
1876
+
1877
+ debug_check_started();
1878
+
1879
+ Data_Get_Struct(self, debug_context_t, debug_context);
1880
+ if (RTEST(value)) {
1881
+ CTX_FL_SET(debug_context, CTX_FL_TRACING);
1882
+ }
1883
+ else {
1884
+ CTX_FL_UNSET(debug_context, CTX_FL_TRACING);
1885
+ }
1886
+ return value;
1887
+ }
1888
+
1889
+ /*
1890
+ * call-seq:
1891
+ * context.ignored? -> bool
1892
+ *
1893
+ * Returns the ignore flag for the current context.
1894
+ */
1895
+ static VALUE
1896
+ context_ignored(VALUE self)
1897
+ {
1898
+ debug_context_t *debug_context;
1899
+
1900
+ debug_check_started();
1901
+
1902
+ Data_Get_Struct(self, debug_context_t, debug_context);
1903
+ return CTX_FL_TEST(debug_context, CTX_FL_IGNORE) ? Qtrue : Qfalse;
1904
+ }
1905
+
1906
+ /*
1907
+ * call-seq:
1908
+ * context.dead? -> bool
1909
+ *
1910
+ * Returns +true+ if context doesn't represent a live context and is created
1911
+ * during post-mortem exception handling.
1912
+ */
1913
+ static VALUE
1914
+ context_dead(VALUE self)
1915
+ {
1916
+ debug_context_t *debug_context;
1917
+
1918
+ debug_check_started();
1919
+
1920
+ Data_Get_Struct(self, debug_context_t, debug_context);
1921
+ return CTX_FL_TEST(debug_context, CTX_FL_DEAD) ? Qtrue : Qfalse;
1922
+ }
1923
+
1924
+ /*
1925
+ * call-seq:
1926
+ * context.stop_reason -> sym
1927
+ *
1928
+ * Returns the reason for the stop. It maybe of the following values:
1929
+ * :initial, :step, :breakpoint, :catchpoint, :post-mortem
1930
+ */
1931
+ static VALUE
1932
+ context_stop_reason(VALUE self)
1933
+ {
1934
+ debug_context_t *debug_context;
1935
+ const char * sym_name;
1936
+
1937
+ debug_check_started();
1938
+
1939
+ Data_Get_Struct(self, debug_context_t, debug_context);
1940
+
1941
+ switch(debug_context->stop_reason) {
1942
+ case CTX_STOP_STEP:
1943
+ sym_name = "step";
1944
+ break;
1945
+ case CTX_STOP_BREAKPOINT:
1946
+ sym_name = "breakpoint";
1947
+ break;
1948
+ case CTX_STOP_CATCHPOINT:
1949
+ sym_name = "catchpoint";
1950
+ break;
1951
+ case CTX_STOP_NONE:
1952
+ default:
1953
+ sym_name = "none";
1954
+ }
1955
+
1956
+ if(CTX_FL_TEST(debug_context, CTX_FL_DEAD)) {
1957
+ sym_name = "post-mortem";
1958
+ }
1959
+
1960
+ return ID2SYM(rb_intern(sym_name));
1961
+ }
1962
+
1963
+ /*
1964
+ * call-seq:
1965
+ * context.jump(line, file) -> bool
1966
+ *
1967
+ * Returns +true+ if jump to +line+ in filename +file+ was successful.
1968
+ */
1969
+ static VALUE
1970
+ context_jump(VALUE self, VALUE line, VALUE file)
1971
+ {
1972
+ return INT2FIX(1);
1973
+
1974
+ #if 0
1975
+ debug_context_t *debug_context;
1976
+ debug_frame_t *debug_frame;
1977
+ int i;
1978
+ rb_thread_t *th;
1979
+ rb_control_frame_t *cfp;
1980
+ rb_control_frame_t *cfp_end;
1981
+ rb_control_frame_t *cfp_start = NULL;
1982
+
1983
+ debug_check_started();
1984
+
1985
+ Data_Get_Struct(self, debug_context_t, debug_context);
1986
+ GetThreadPtr(context_thread_0(debug_context), th);
1987
+ debug_frame = get_top_frame(debug_context);
1988
+ if (debug_frame == NULL)
1989
+ rb_raise(rb_eRuntimeError, "No frames collected.");
1990
+
1991
+ line = FIX2INT(line);
1992
+
1993
+ /* find topmost frame of the debugged code */
1994
+ cfp = th->cfp;
1995
+ cfp_end = RUBY_VM_END_CONTROL_FRAME(th);
1996
+ while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, cfp_end))
1997
+ {
1998
+ if (cfp->pc == debug_frame->info.runtime.last_pc)
1999
+ {
2000
+ cfp_start = cfp;
2001
+ if ((cfp->pc - cfp->iseq->iseq_encoded) >= (cfp->iseq->iseq_size - 1))
2002
+ return(INT2FIX(1)); /* no space for opt_call_c_function hijack */
2003
+ break;
2004
+ }
2005
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
2006
+ }
2007
+ if (cfp_start == NULL)
2008
+ return(INT2FIX(2)); /* couldn't find frame; should never happen */
2009
+
2010
+ /* find target frame to jump to */
2011
+ while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, cfp_end))
2012
+ {
2013
+ if ((cfp->iseq != NULL) && (rb_str_cmp(file, cfp->iseq->filename) == 0))
2014
+ {
2015
+ for (i = 0; i < cfp->iseq->insn_info_size; i++)
2016
+ {
2017
+ if (cfp->iseq->insn_info_table[i].line_no != line)
2018
+ continue;
2019
+
2020
+ /* hijack the currently running code so that we can change the frame PC */
2021
+ debug_context->saved_jump_ins[0] = cfp_start->pc[0];
2022
+ debug_context->saved_jump_ins[1] = cfp_start->pc[1];
2023
+ cfp_start->pc[0] = opt_call_c_function;
2024
+ cfp_start->pc[1] = (VALUE)do_jump;
2025
+
2026
+ debug_context->jump_cfp = cfp;
2027
+ debug_context->jump_pc =
2028
+ cfp->iseq->iseq_encoded + cfp->iseq->insn_info_table[i].position;
2029
+
2030
+ return(INT2FIX(0)); /* success */
2031
+ }
2032
+ }
2033
+
2034
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
2035
+ }
2036
+
2037
+ return(INT2FIX(3)); /* couldn't find a line and file frame match */
2038
+ #endif
2039
+ }
2040
+
2041
+ /*
2042
+ * call-seq:
2043
+ * context.break -> bool
2044
+ *
2045
+ * Returns +true+ if context is currently running and set flag to break it at next line
2046
+ */
2047
+ static VALUE
2048
+ context_pause(VALUE self)
2049
+ {
2050
+ debug_context_t *debug_context;
2051
+
2052
+ debug_check_started();
2053
+
2054
+ Data_Get_Struct(self, debug_context_t, debug_context);
2055
+
2056
+ if (CTX_FL_TEST(debug_context, CTX_FL_DEAD)) {
2057
+ return(Qfalse);
2058
+ }
2059
+
2060
+ if (debug_context->thread_id == rb_thread_current()) {
2061
+ return(Qfalse);
2062
+ }
2063
+
2064
+ debug_context->thread_pause = 1;
2065
+ return(Qtrue);
2066
+ }
2067
+
2068
+ /*
2069
+ * Document-class: Context
2070
+ *
2071
+ * == Summary
2072
+ *
2073
+ * Debugger keeps a single instance of this class for each Ruby thread.
2074
+ */
2075
+ static void
2076
+ Init_context(void)
2077
+ {
2078
+ cContext = rb_define_class_under(mDebugger, "Context", rb_cObject);
2079
+ rb_define_method(cContext, "stop_next=", context_stop_next, -1);
2080
+ rb_define_method(cContext, "step", context_stop_next, -1);
2081
+ rb_define_method(cContext, "step_over", context_step_over, -1);
2082
+ rb_define_method(cContext, "stop_frame=", context_stop_frame, 1);
2083
+ rb_define_method(cContext, "thread", context_thread, 0);
2084
+ rb_define_method(cContext, "thnum", context_thnum, 0);
2085
+ rb_define_method(cContext, "stop_reason", context_stop_reason, 0);
2086
+ rb_define_method(cContext, "suspend", context_suspend, 0);
2087
+ rb_define_method(cContext, "suspended?", context_is_suspended, 0);
2088
+ rb_define_method(cContext, "resume", context_resume, 0);
2089
+ rb_define_method(cContext, "tracing", context_tracing, 0);
2090
+ rb_define_method(cContext, "tracing=", context_set_tracing, 1);
2091
+ rb_define_method(cContext, "ignored?", context_ignored, 0);
2092
+ rb_define_method(cContext, "frame_args", context_frame_args, -1);
2093
+ rb_define_method(cContext, "frame_args_info", context_frame_args_info, -1);
2094
+ rb_define_method(cContext, "frame_binding", context_frame_binding, -1);
2095
+ rb_define_method(cContext, "frame_class", context_frame_class, -1);
2096
+ rb_define_method(cContext, "frame_file", context_frame_file, -1);
2097
+ rb_define_method(cContext, "frame_id", context_frame_id, -1);
2098
+ rb_define_method(cContext, "frame_line", context_frame_line, -1);
2099
+ rb_define_method(cContext, "frame_locals", context_frame_locals, -1);
2100
+ rb_define_method(cContext, "frame_method", context_frame_id, -1);
2101
+ rb_define_method(cContext, "frame_self", context_frame_self, -1);
2102
+ rb_define_method(cContext, "stack_size", context_stack_size, 0);
2103
+ rb_define_method(cContext, "dead?", context_dead, 0);
2104
+ rb_define_method(cContext, "breakpoint", context_breakpoint, 0); /* in breakpoint.c */
2105
+ rb_define_method(cContext, "set_breakpoint", context_set_breakpoint, -1); /* in breakpoint.c */
2106
+ rb_define_method(cContext, "jump", context_jump, 2);
2107
+ rb_define_method(cContext, "pause", context_pause, 0);
2108
+ }
2109
+
2110
+ /*
2111
+ * call-seq:
2112
+ * Debugger.breakpoints -> array
2113
+ *
2114
+ * Returns an array of breakpoints.
2115
+ */
2116
+ static VALUE
2117
+ debug_breakpoints(VALUE self)
2118
+ {
2119
+ debug_check_started();
2120
+
2121
+ return rdebug_breakpoints;
2122
+ }
2123
+
2124
+ /*
2125
+ * call-seq:
2126
+ * Debugger.add_breakpoint(source, pos, condition = nil) -> breakpoint
2127
+ *
2128
+ * Adds a new breakpoint.
2129
+ * <i>source</i> is a name of a file or a class.
2130
+ * <i>pos</i> is a line number or a method name if <i>source</i> is a class name.
2131
+ * <i>condition</i> is a string which is evaluated to +true+ when this breakpoint
2132
+ * is activated.
2133
+ */
2134
+ static VALUE
2135
+ debug_add_breakpoint(int argc, VALUE *argv, VALUE self)
2136
+ {
2137
+ VALUE result;
2138
+
2139
+ debug_check_started();
2140
+
2141
+ result = create_breakpoint_from_args(argc, argv, ++bkp_count);
2142
+ rb_ary_push(rdebug_breakpoints, result);
2143
+ return result;
2144
+ }
2145
+
2146
+ /*
2147
+ * Document-class: Debugger
2148
+ *
2149
+ * == Summary
2150
+ *
2151
+ * This is a singleton class allows controlling the debugger. Use it to start/stop debugger,
2152
+ * set/remove breakpoints, etc.
2153
+ */
2154
+ void
2155
+ Init_ruby_debug(void)
2156
+ {
2157
+ mDebugger = rb_define_module("Debugger");
2158
+ rb_define_const(mDebugger, "VERSION", rb_str_new2(DEBUG_VERSION));
2159
+ rb_define_module_function(mDebugger, "start_", debug_start, 0);
2160
+ rb_define_module_function(mDebugger, "stop", debug_stop, 0);
2161
+ rb_define_module_function(mDebugger, "started?", debug_is_started, 0);
2162
+ rb_define_module_function(mDebugger, "breakpoints", debug_breakpoints, 0);
2163
+ rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, -1);
2164
+ rb_define_module_function(mDebugger, "remove_breakpoint", rdebug_remove_breakpoint, 1); /* in breakpoint.c */
2165
+ rb_define_module_function(mDebugger, "add_catchpoint", rdebug_add_catchpoint, 1); /* in breakpoint.c */
2166
+ rb_define_module_function(mDebugger, "catchpoints", debug_catchpoints, 0); /* in breakpoint.c */
2167
+ rb_define_module_function(mDebugger, "last_context", debug_last_interrupted, 0);
2168
+ rb_define_module_function(mDebugger, "contexts", debug_contexts, 0);
2169
+ rb_define_module_function(mDebugger, "current_context", debug_current_context, 0);
2170
+ rb_define_module_function(mDebugger, "thread_context", debug_thread_context, 1);
2171
+ rb_define_module_function(mDebugger, "suspend", debug_suspend, 0);
2172
+ rb_define_module_function(mDebugger, "resume", debug_resume, 0);
2173
+ rb_define_module_function(mDebugger, "tracing", debug_tracing, 0);
2174
+ rb_define_module_function(mDebugger, "tracing=", debug_set_tracing, 1);
2175
+ rb_define_module_function(mDebugger, "debug_load", debug_debug_load, -1);
2176
+ rb_define_module_function(mDebugger, "skip", debug_skip, 0);
2177
+ rb_define_module_function(mDebugger, "debug_at_exit", debug_at_exit, 0);
2178
+ rb_define_module_function(mDebugger, "post_mortem?", debug_post_mortem, 0);
2179
+ rb_define_module_function(mDebugger, "post_mortem=", debug_set_post_mortem, 1);
2180
+ rb_define_module_function(mDebugger, "keep_frame_binding?", debug_keep_frame_binding, 0);
2181
+ rb_define_module_function(mDebugger, "keep_frame_binding=", debug_set_keep_frame_binding, 1);
2182
+ rb_define_module_function(mDebugger, "track_frame_args?", debug_track_frame_args, 0);
2183
+ rb_define_module_function(mDebugger, "track_frame_args=", debug_set_track_frame_args, 1);
2184
+ rb_define_module_function(mDebugger, "debug", debug_debug, 0);
2185
+ rb_define_module_function(mDebugger, "debug=", debug_set_debug, 1);
2186
+
2187
+ cThreadsTable = rb_define_class_under(mDebugger, "ThreadsTable", rb_cObject);
2188
+
2189
+ cDebugThread = rb_define_class_under(mDebugger, "DebugThread", rb_cThread);
2190
+ rb_define_singleton_method(cDebugThread, "inherited",
2191
+ debug_thread_inherited, 1);
2192
+
2193
+ Init_context();
2194
+ Init_breakpoint();
2195
+
2196
+ rb_global_variable(&last_context);
2197
+ rb_global_variable(&last_thread);
2198
+ rb_global_variable(&locker);
2199
+ rb_global_variable(&rdebug_breakpoints);
2200
+ rb_global_variable(&rdebug_catchpoints);
2201
+ rb_global_variable(&rdebug_threads_tbl);
2202
+ rb_global_variable(&tracepoints);
2203
+ }