Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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