debugger2 1.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ }