Skip to content

HTTPS clone URL

Subversion checkout URL

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