debugger 1.0.0.rc1

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