Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 429 lines (388 sloc) 10.07 kB
cf73725 experimental debugger
Laurent Sansonetti authored
1 /*
2 * MacRuby debugger.
3 *
4 * This file is covered by the Ruby license. See COPYING for more details.
5 *
9595725 update copyrights to 2011
Laurent Sansonetti authored
6 * Copyright (C) 2010-2011, Apple Inc. All rights reserved.
cf73725 experimental debugger
Laurent Sansonetti authored
7 */
8
a6a5d3e started to trim out the static library
Laurent Sansonetti authored
9 #if !defined(MACRUBY_STATIC)
10
cf73725 experimental debugger
Laurent Sansonetti authored
11 #include <llvm/Module.h>
12 #include <llvm/DerivedTypes.h>
13 #include <llvm/Constants.h>
14 #include <llvm/CallingConv.h>
15 #include <llvm/Instructions.h>
16 #include <llvm/Intrinsics.h>
17 #include <llvm/Analysis/DebugInfo.h>
9be9a37 now depend on revision 127367 of branch LLVM 2.9
Laurent Sansonetti authored
18 #if !defined(LLVM_TOT)
c3fac48 add support for llvm 2.9, test it by passing CFLAGS=-D__SUPPORT_LLVM_…
Laurent Sansonetti authored
19 # include <llvm/Analysis/DIBuilder.h>
20 #endif
cf73725 experimental debugger
Laurent Sansonetti authored
21 #include <llvm/ExecutionEngine/JIT.h>
22 #include <llvm/PassManager.h>
23 #include <llvm/Target/TargetData.h>
24 using namespace llvm;
25
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29
d0898dd include/ruby/macruby.h -> macruby_internal.h
Laurent Sansonetti authored
30 #include "macruby_internal.h"
cf73725 experimental debugger
Laurent Sansonetti authored
31 #include "ruby/node.h"
32 #include "vm.h"
33 #include "compiler.h"
34 #include "debugger.h"
35
36 void
37 RoxorCompiler::compile_debug_trap(void)
38 {
39 if (bb == NULL || inside_eval) {
40 return;
41 }
42
43 if (debugTrapFunc == NULL) {
44 // void rb_vm_debug_trap(const char *file, int line,
45 // VALUE self, rb_vm_block_t *current_block, int lvars_size, ...);
46 std::vector<const Type *> types;
47 types.push_back(PtrTy);
48 types.push_back(Int32Ty);
49 types.push_back(RubyObjTy);
50 types.push_back(PtrTy);
51 types.push_back(Int32Ty);
52 FunctionType *ft = FunctionType::get(VoidTy, types, true);
53 debugTrapFunc = cast<Function>
54 (module->getOrInsertFunction( "rb_vm_debug_trap", ft));
55 }
56
57 std::vector<Value *> params;
58 params.push_back(compile_const_pointer((void *)fname));
59 params.push_back(ConstantInt::get(Int32Ty, current_line));
60 params.push_back(current_self);
61 params.push_back(running_block == NULL
62 ? compile_const_pointer(NULL) : running_block);
63
64 // Lvars.
65 params.push_back(ConstantInt::get(Int32Ty, (int)lvars.size()));
66 for (std::map<ID, Value *>::iterator iter = lvars.begin();
67 iter != lvars.end(); ++iter) {
68
69 ID lvar = iter->first;
70 params.push_back(compile_id(lvar));
71 params.push_back(iter->second);
72 }
73
74 CallInst::Create(debugTrapFunc, params.begin(), params.end(), "", bb);
75 }
76
77 extern "C"
78 void
79 rb_vm_debug_trap(const char *file, const int line, VALUE self,
80 rb_vm_block_t *current_block, int lvars_size, ...)
81 {
82 if (RoxorDebugger::shared == NULL) {
83 RoxorDebugger::shared = RoxorDebugger::unix_server();
84 }
85
86 va_list args;
87 va_start(args, lvars_size);
88 RoxorDebugger::shared->trap(file, line, self, current_block,
89 lvars_size, args);
90 va_end(args);
91 }
92
93 unsigned int RoxorBreakPoint::IDs = 0;
94 RoxorDebugger *RoxorDebugger::shared = NULL;
95
96 RoxorDebugger *
97 RoxorDebugger::unix_server(void)
98 {
99 // Create the socket.
100 const int fd = socket(PF_LOCAL, SOCK_STREAM, 0);
101 if (fd < 0) {
102 perror("socket()");
103 exit(1);
104 }
105
106 // Bind it to the given path.
107 assert(ruby_debug_socket_path != Qfalse);
108 const char *path = RSTRING_PTR(ruby_debug_socket_path);
109 struct sockaddr_un name;
110 name.sun_family = PF_LOCAL;
111 strncpy(name.sun_path, path, sizeof(name.sun_path));
112 if (bind(fd, (struct sockaddr *)&name, SUN_LEN(&name)) != 0) {
113 perror("bind()");
114 exit(1);
115 }
116
117 // Prepare for listening, backlog of 1 connection.
118 if (listen(fd, 1) != 0) {
119 perror("listen()");
120 exit(1);
121 }
122
123 // Accept first connection.
124 struct sockaddr_un remote;
125 socklen_t remotelen = sizeof(remote);
126 const int pipe = accept(fd, (struct sockaddr *)&remote, &remotelen);
127 if (socket < 0) {
128 perror("accept()");
129 exit(1);
130 }
131
132 //printf("DBG: connected with fd %d\n", pipe);
133
134 return new RoxorDebugger(pipe);
135 }
136
137 RoxorDebugger::RoxorDebugger(int _pipe)
138 {
139 pipe = _pipe;
140 binding = NULL;
141 breakpoint = NULL;
142 frame = 0;
143
144 // We want to break at the very first line.
145 break_at_next = true;
146 }
147
148 RoxorDebugger::~RoxorDebugger()
149 {
150 close(pipe);
151 }
152
153 bool
154 RoxorDebugger::send(std::string &data)
155 {
156 //printf("DBG: send %s\n", data.c_str());
157 const ssize_t len = ::send(pipe, data.c_str(), data.length(), 0);
158 if (len != (ssize_t)data.length()) {
159 if (len == -1) {
160 perror("send()");
161 }
162 else {
163 fprintf(stderr, "expected to send %ld bytes instead of %ld\n",
164 data.length(), len);
165 }
166 return false;
167 }
168 return true;
169 }
170
171 bool
172 RoxorDebugger::recv(std::string &data)
173 {
174 char buf[512];
175 const ssize_t len = ::recv(pipe, buf, sizeof buf, 0);
176 if (len == -1) {
177 perror("recv()");
178 return false;
179 }
180 data.clear();
181 data.append(buf, len);
182 //printf("DBG: recv %s\n", data.c_str());
183 return true;
184 }
185
186 void
187 RoxorDebugger::trap(const char *file, const int line, VALUE self,
188 rb_vm_block_t *block, int lvars_size, va_list lvars)
189 {
190 // Compute location.
191 char buf[100];
192 snprintf(buf, sizeof buf, "%s:%d", file, line);
193 std::string loc(buf);
194
195 // Should we break here?
196 bool should_break = false;
197 RoxorBreakPoint *bp = NULL;
198 if (break_at_next) {
199 should_break = true;
200 break_at_next = false;
201 }
202 else {
203 std::map<std::string, RoxorBreakPoint *>::iterator iter =
204 breakpoints.find(loc);
205 if (iter != breakpoints.end()) {
206 bp = iter->second;
207 if (bp->enabled) {
208 if (bp->condition.length() > 0) {
209 VALUE obj = evaluate_expression(self, block, lvars_size,
210 lvars, bp->condition);
211 if (obj != Qnil && obj != Qfalse) {
212 should_break = true;
213 }
214 }
215 else {
216 should_break = true;
217 }
218 }
219 }
220 }
221
222 if (!should_break) {
223 return;
224 }
225
226 // Send the current location to the client.
227 if (!send(loc)) {
228 return;
229 }
230
231 // Invalidate latest binding.
232 if (binding != NULL) {
233 GC_RELEASE(binding);
234 binding = NULL;
235 }
236 frame = 0;
237
238 // Enter the main loop.
239 std::string data;
240 while (true) {
241 // Receive command.
242 if (!recv(data)) {
243 return;
244 }
245 const char *cmd = data.c_str();
246
247 // Handle command.
248 if (strcmp(cmd, "exit") == 0) {
249 exit(0);
250 }
251 if (strcmp(cmd, "continue") == 0) {
252 break;
253 }
254 if (strcmp(cmd, "next") == 0) {
255 break_at_next = true;
256 break;
257 }
258 if (strncmp(cmd, "break ", 6) == 0) {
259 std::string location = data.substr(6);
260 if (location.length() >= 3) {
261 unsigned int id = add_breakpoint(location);
262 char buf[10];
263 snprintf(buf, sizeof buf, "%d", id);
264 send(buf);
265 continue;
266 }
267 }
268 if (strncmp(cmd, "enable ", 7) == 0) {
269 const unsigned int bpid = (unsigned int)strtol(cmd + 7, NULL, 10);
270 RoxorBreakPoint *bp = find_breakpoint(bpid);
271 if (bp != NULL) {
272 bp->enabled = true;
273 }
274 continue;
275 }
276 if (strncmp(cmd, "disable ", 8) == 0) {
277 const unsigned int bpid = (unsigned int)strtol(cmd + 8, NULL, 10);
278 RoxorBreakPoint *bp = find_breakpoint(bpid);
279 if (bp != NULL) {
280 bp->enabled = false;
281 }
282 continue;
283 }
284 if (strncmp(cmd, "delete ", 7) == 0) {
285 const unsigned int bpid = (unsigned int)strtol(cmd + 7, NULL, 10);
286 delete_breakpoint(bpid);
287 continue;
288 }
289 if (strncmp(cmd, "condition ", 10) == 0) {
290 const char *p = strchr(cmd + 10, ' ');
291 if (p != NULL) {
292 std::string bpstr = data.substr(10, p - cmd - 10);
293 const unsigned int bpid = (unsigned int)strtol(bpstr.c_str(),
294 NULL, 10);
295 std::string condstr = data.substr(10 + bpstr.length());
296 RoxorBreakPoint *bp = find_breakpoint(bpid);
297 if (bp != NULL) {
298 bp->condition = condstr;
299 }
300 continue;
301 }
302 }
303 if (strcmp(cmd, "info breakpoints") == 0) {
304 std::string resp;
305 for (std::map<std::string, RoxorBreakPoint *>::iterator iter
306 = breakpoints.begin();
307 iter != breakpoints.end();
308 ++iter) {
309 char buf[100];
310 snprintf(buf, sizeof buf, "id=%d,location=%s,enabled=%d\n",
311 iter->second->id, iter->first.c_str(),
312 iter->second->enabled);
313 resp.append(buf);
314 }
315 if (resp.length() == 0) {
316 resp.append("nil");
317 }
318 send(resp);
319 continue;
320 }
321 if (strcmp(cmd, "backtrace") == 0) {
0849733 Kernel#caller: now honor the skip argument
Laurent Sansonetti authored
322 VALUE bt = rb_vm_backtrace(0);
cf73725 experimental debugger
Laurent Sansonetti authored
323 VALUE str = rb_ary_join(bt, rb_str_new2("\n"));
324 send(RSTRING_PTR(str));
325 continue;
326 }
327 if (strncmp(cmd, "frame ", 6) == 0) {
328 unsigned int frid = (unsigned int)strtol(cmd + 6, NULL, 10);
329 if (frame != frid) {
330 frame = frid;
331 if (binding != NULL) {
332 GC_RELEASE(binding);
333 binding = NULL;
334 }
335 // TODO update location
336 }
337 continue;
338 }
339 if (strncmp(cmd, "eval ", 5) == 0) {
340 std::string expr = data.substr(5);
341 if (expr.length() > 0) {
342 VALUE obj = evaluate_expression(self, block, lvars_size, lvars,
343 expr);
344 send(RSTRING_PTR(rb_inspect(obj)));
345 continue;
346 }
347 }
348
349 // Unknown command, let's exit the program. This should never happen!
350 fprintf(stderr, "unknown command: %s\n", cmd);
351 exit(1);
352 }
353 }
354
355 unsigned int
356 RoxorDebugger::add_breakpoint(std::string &location)
357 {
358 std::map<std::string, RoxorBreakPoint *>::iterator iter
359 = breakpoints.find(location);
360
361 RoxorBreakPoint *bp;
362 if (iter == breakpoints.end()) {
363 bp = new RoxorBreakPoint();
364 breakpoints[location] = bp;
365 }
366 else {
367 bp = iter->second;
368 }
369
370 return bp->id;
371 }
372
373 VALUE
374 RoxorDebugger::evaluate_expression(VALUE self, rb_vm_block_t *block,
375 int lvars_size, va_list lvars, std::string expr)
376 {
377 if (binding == NULL) {
378 if (frame == 0) {
7b14592 @takaokouji fixed below.
takaokouji authored
379 binding = rb_vm_create_binding(self, block, NULL, NULL, lvars_size, lvars,
cf73725 experimental debugger
Laurent Sansonetti authored
380 false);
381 }
382 else {
383 binding = GET_VM()->get_binding(frame - 1);
384 assert(binding != NULL);
385 }
386 GC_RETAIN(binding);
387 }
388
389 try {
390 return rb_vm_eval_string(self, 0, rb_str_new2(expr.c_str()), binding,
68ac3fc @takaokouji fixed RoxorVM::pop_outer() leaks memory. (fixes #1267)
takaokouji authored
391 "(eval)", 1, false);
cf73725 experimental debugger
Laurent Sansonetti authored
392 }
393 catch (...) {
394 rb_vm_print_current_exception();
395 return Qnil;
396 }
397 }
398
399 RoxorBreakPoint *
400 RoxorDebugger::find_breakpoint(unsigned int bpid)
401 {
402 for (std::map<std::string, RoxorBreakPoint *>::iterator iter
403 = breakpoints.begin();
404 iter != breakpoints.end();
405 ++iter) {
406 if (iter->second->id == bpid) {
407 return iter->second;
408 }
409 }
410 return NULL;
411 }
412
413 bool
414 RoxorDebugger::delete_breakpoint(unsigned int bpid)
415 {
416 for (std::map<std::string, RoxorBreakPoint *>::iterator iter
417 = breakpoints.begin();
418 iter != breakpoints.end();
419 ++iter) {
420 if (iter->second->id == bpid) {
421 breakpoints.erase(iter);
422 return true;
423 }
424 }
425 return false;
426 }
a6a5d3e started to trim out the static library
Laurent Sansonetti authored
427
428 #endif // !MACRUBY_STATIC
Something went wrong with that request. Please try again.