Ruby
2.0.0p247(2013-06-27revision41674)
|
00001 /********************************************************************** 00002 00003 vm_trace.c - 00004 00005 $Author: ko1 $ 00006 created at: Tue Aug 14 19:37:09 2012 00007 00008 Copyright (C) 1993-2012 Yukihiro Matsumoto 00009 00010 **********************************************************************/ 00011 00012 /* 00013 * This file incldue two parts: 00014 * 00015 * (1) set_trace_func internal mechanisms 00016 * and C level API 00017 * 00018 * (2) Ruby level API 00019 * (2-1) set_trace_func API 00020 * (2-2) TracePoint API (not yet) 00021 * 00022 */ 00023 00024 #include "ruby/ruby.h" 00025 #include "ruby/debug.h" 00026 #include "ruby/encoding.h" 00027 00028 #include "internal.h" 00029 #include "vm_core.h" 00030 #include "eval_intern.h" 00031 00032 /* (1) trace mechanisms */ 00033 00034 typedef struct rb_event_hook_struct { 00035 rb_event_hook_flag_t hook_flags; 00036 rb_event_flag_t events; 00037 rb_event_hook_func_t func; 00038 VALUE data; 00039 struct rb_event_hook_struct *next; 00040 } rb_event_hook_t; 00041 00042 typedef void (*rb_event_hook_raw_arg_func_t)(VALUE data, const rb_trace_arg_t *arg); 00043 00044 #define MAX_EVENT_NUM 32 00045 00046 static int ruby_event_flag_count[MAX_EVENT_NUM] = {0}; 00047 00048 /* called from vm.c */ 00049 00050 void 00051 vm_trace_mark_event_hooks(rb_hook_list_t *hooks) 00052 { 00053 rb_event_hook_t *hook = hooks->hooks; 00054 00055 while (hook) { 00056 rb_gc_mark(hook->data); 00057 hook = hook->next; 00058 } 00059 } 00060 00061 /* ruby_vm_event_flags management */ 00062 00063 static void 00064 recalc_add_ruby_vm_event_flags(rb_event_flag_t events) 00065 { 00066 int i; 00067 ruby_vm_event_flags = 0; 00068 00069 for (i=0; i<MAX_EVENT_NUM; i++) { 00070 if (events & (1 << i)) { 00071 ruby_event_flag_count[i]++; 00072 } 00073 ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0; 00074 } 00075 } 00076 00077 static void 00078 recalc_remove_ruby_vm_event_flags(rb_event_flag_t events) 00079 { 00080 int i; 00081 ruby_vm_event_flags = 0; 00082 00083 for (i=0; i<MAX_EVENT_NUM; i++) { 00084 if (events & (1 << i)) { 00085 ruby_event_flag_count[i]--; 00086 } 00087 ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0; 00088 } 00089 } 00090 00091 /* add/remove hooks */ 00092 00093 static rb_thread_t * 00094 thval2thread_t(VALUE thval) 00095 { 00096 rb_thread_t *th; 00097 GetThreadPtr(thval, th); 00098 return th; 00099 } 00100 00101 static rb_event_hook_t * 00102 alloc_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags) 00103 { 00104 rb_event_hook_t *hook = ALLOC(rb_event_hook_t); 00105 hook->hook_flags = hook_flags; 00106 hook->events = events; 00107 hook->func = func; 00108 hook->data = data; 00109 return hook; 00110 } 00111 00112 static void 00113 connect_event_hook(rb_hook_list_t *list, rb_event_hook_t *hook) 00114 { 00115 hook->next = list->hooks; 00116 list->hooks = hook; 00117 recalc_add_ruby_vm_event_flags(hook->events); 00118 list->events |= hook->events; 00119 } 00120 00121 static void 00122 rb_threadptr_add_event_hook(rb_thread_t *th, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags) 00123 { 00124 rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags); 00125 connect_event_hook(&th->event_hooks, hook); 00126 } 00127 00128 void 00129 rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) 00130 { 00131 rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data, RUBY_EVENT_HOOK_FLAG_SAFE); 00132 } 00133 00134 void 00135 rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data) 00136 { 00137 rb_event_hook_t *hook = alloc_event_hook(func, events, data, RUBY_EVENT_HOOK_FLAG_SAFE); 00138 connect_event_hook(&GET_VM()->event_hooks, hook); 00139 } 00140 00141 void 00142 rb_thread_add_event_hook2(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags) 00143 { 00144 rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data, hook_flags); 00145 } 00146 00147 void 00148 rb_add_event_hook2(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags) 00149 { 00150 rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags); 00151 connect_event_hook(&GET_VM()->event_hooks, hook); 00152 } 00153 00154 /* if func is 0, then clear all funcs */ 00155 static int 00156 remove_event_hook(rb_hook_list_t *list, rb_event_hook_func_t func, VALUE data) 00157 { 00158 int ret = 0; 00159 rb_event_hook_t *hook = list->hooks; 00160 00161 while (hook) { 00162 if (func == 0 || hook->func == func) { 00163 if (data == Qundef || hook->data == data) { 00164 hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED; 00165 ret+=1; 00166 list->need_clean++; 00167 } 00168 } 00169 hook = hook->next; 00170 } 00171 00172 return ret; 00173 } 00174 00175 static int 00176 rb_threadptr_remove_event_hook(rb_thread_t *th, rb_event_hook_func_t func, VALUE data) 00177 { 00178 return remove_event_hook(&th->event_hooks, func, data); 00179 } 00180 00181 int 00182 rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func) 00183 { 00184 return rb_threadptr_remove_event_hook(thval2thread_t(thval), func, Qundef); 00185 } 00186 00187 int 00188 rb_thread_remove_event_hook_with_data(VALUE thval, rb_event_hook_func_t func, VALUE data) 00189 { 00190 return rb_threadptr_remove_event_hook(thval2thread_t(thval), func, data); 00191 } 00192 00193 int 00194 rb_remove_event_hook(rb_event_hook_func_t func) 00195 { 00196 return remove_event_hook(&GET_VM()->event_hooks, func, Qundef); 00197 } 00198 00199 int 00200 rb_remove_event_hook_with_data(rb_event_hook_func_t func, VALUE data) 00201 { 00202 return remove_event_hook(&GET_VM()->event_hooks, func, data); 00203 } 00204 00205 static int 00206 clear_trace_func_i(st_data_t key, st_data_t val, st_data_t flag) 00207 { 00208 rb_thread_t *th; 00209 GetThreadPtr((VALUE)key, th); 00210 rb_threadptr_remove_event_hook(th, 0, Qundef); 00211 return ST_CONTINUE; 00212 } 00213 00214 void 00215 rb_clear_trace_func(void) 00216 { 00217 st_foreach(GET_VM()->living_threads, clear_trace_func_i, (st_data_t) 0); 00218 rb_remove_event_hook(0); 00219 } 00220 00221 /* invoke hooks */ 00222 00223 static void 00224 clean_hooks(rb_hook_list_t *list) 00225 { 00226 rb_event_hook_t *hook, **nextp = &list->hooks; 00227 00228 list->events = 0; 00229 list->need_clean = 0; 00230 00231 while ((hook = *nextp) != 0) { 00232 if (hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) { 00233 *nextp = hook->next; 00234 recalc_remove_ruby_vm_event_flags(hook->events); 00235 xfree(hook); 00236 } 00237 else { 00238 list->events |= hook->events; /* update active events */ 00239 nextp = &hook->next; 00240 } 00241 } 00242 } 00243 00244 static int 00245 exec_hooks(rb_thread_t *th, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg, int can_clean_hooks) 00246 { 00247 int state; 00248 volatile int raised; 00249 00250 if (UNLIKELY(list->need_clean > 0) && can_clean_hooks) { 00251 clean_hooks(list); 00252 } 00253 00254 raised = rb_threadptr_reset_raised(th); 00255 00256 /* TODO: Support !RUBY_EVENT_HOOK_FLAG_SAFE hooks */ 00257 00258 TH_PUSH_TAG(th); 00259 if ((state = TH_EXEC_TAG()) == 0) { 00260 rb_event_hook_t *hook; 00261 00262 for (hook = list->hooks; hook; hook = hook->next) { 00263 if (LIKELY(!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED)) && (trace_arg->event & hook->events)) { 00264 if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_RAW_ARG)) { 00265 (*hook->func)(trace_arg->event, hook->data, trace_arg->self, trace_arg->id, trace_arg->klass); 00266 } 00267 else { 00268 (*((rb_event_hook_raw_arg_func_t)hook->func))(hook->data, trace_arg); 00269 } 00270 } 00271 } 00272 } 00273 TH_POP_TAG(); 00274 00275 if (raised) { 00276 rb_threadptr_set_raised(th); 00277 } 00278 00279 return state; 00280 } 00281 00282 static void 00283 rb_threadptr_exec_event_hooks_orig(rb_trace_arg_t *trace_arg, int pop_p) 00284 { 00285 rb_thread_t *th = trace_arg->th; 00286 if (th->trace_arg == 0 && 00287 trace_arg->self != rb_mRubyVMFrozenCore /* skip special methods. TODO: remove it. */) { 00288 const int vm_tracing = th->vm->trace_running; 00289 const VALUE errinfo = th->errinfo; 00290 const int outer_state = th->state; 00291 int state = 0; 00292 th->state = 0; 00293 00294 th->vm->trace_running++; 00295 th->trace_arg = trace_arg; 00296 { 00297 rb_hook_list_t *list; 00298 00299 /* thread local traces */ 00300 list = &th->event_hooks; 00301 if (list->events & trace_arg->event) { 00302 state = exec_hooks(th, list, trace_arg, TRUE); 00303 if (state) goto terminate; 00304 } 00305 00306 /* vm global traces */ 00307 list = &th->vm->event_hooks; 00308 if (list->events & trace_arg->event) { 00309 state = exec_hooks(th, list, trace_arg, !vm_tracing); 00310 if (state) goto terminate; 00311 } 00312 th->errinfo = errinfo; 00313 } 00314 terminate: 00315 th->trace_arg = 0; 00316 th->vm->trace_running--; 00317 00318 if (state) { 00319 if (pop_p) { 00320 if (VM_FRAME_TYPE_FINISH_P(th->cfp)) { 00321 th->tag = th->tag->prev; 00322 } 00323 th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); 00324 } 00325 TH_JUMP_TAG(th, state); 00326 } 00327 th->state = outer_state; 00328 } 00329 } 00330 00331 void 00332 rb_threadptr_exec_event_hooks_and_pop_frame(rb_trace_arg_t *trace_arg) 00333 { 00334 rb_threadptr_exec_event_hooks_orig(trace_arg, 1); 00335 } 00336 00337 void 00338 rb_threadptr_exec_event_hooks(rb_trace_arg_t *trace_arg) 00339 { 00340 rb_threadptr_exec_event_hooks_orig(trace_arg, 0); 00341 } 00342 00343 VALUE 00344 rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg) 00345 { 00346 volatile int raised; 00347 volatile int outer_state; 00348 VALUE result = Qnil; 00349 rb_thread_t *th = GET_THREAD(); 00350 int state; 00351 const int tracing = th->trace_arg ? 1 : 0; 00352 rb_trace_arg_t dummy_trace_arg; 00353 00354 if (!tracing) th->vm->trace_running++; 00355 if (!th->trace_arg) th->trace_arg = &dummy_trace_arg; 00356 00357 raised = rb_threadptr_reset_raised(th); 00358 outer_state = th->state; 00359 th->state = 0; 00360 00361 TH_PUSH_TAG(th); 00362 if ((state = TH_EXEC_TAG()) == 0) { 00363 result = (*func)(arg); 00364 } 00365 TH_POP_TAG(); 00366 00367 if (raised) { 00368 rb_threadptr_set_raised(th); 00369 } 00370 00371 if (th->trace_arg == &dummy_trace_arg) th->trace_arg = 0; 00372 if (!tracing) th->vm->trace_running--; 00373 00374 if (state) { 00375 JUMP_TAG(state); 00376 } 00377 00378 th->state = outer_state; 00379 return result; 00380 } 00381 00382 static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALUE klass); 00383 00384 /* (2-1) set_trace_func (old API) */ 00385 00386 /* 00387 * call-seq: 00388 * set_trace_func(proc) -> proc 00389 * set_trace_func(nil) -> nil 00390 * 00391 * Establishes _proc_ as the handler for tracing, or disables 00392 * tracing if the parameter is +nil+. 00393 * 00394 * _proc_ takes up to six parameters: 00395 * 00396 * * an event name 00397 * * a filename 00398 * * a line number 00399 * * an object id 00400 * * a binding 00401 * * the name of a class 00402 * 00403 * _proc_ is invoked whenever an event occurs. 00404 * 00405 * Events are: 00406 * 00407 * +c-call+:: call a C-language routine 00408 * +c-return+:: return from a C-language routine 00409 * +call+:: call a Ruby method 00410 * +class+:: start a class or module definition), 00411 * +end+:: finish a class or module definition), 00412 * +line+:: execute code on a new line 00413 * +raise+:: raise an exception 00414 * +return+:: return from a Ruby method 00415 * 00416 * Tracing is disabled within the context of _proc_. 00417 * 00418 * class Test 00419 * def test 00420 * a = 1 00421 * b = 2 00422 * end 00423 * end 00424 * 00425 * set_trace_func proc { |event, file, line, id, binding, classname| 00426 * printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname 00427 * } 00428 * t = Test.new 00429 * t.test 00430 * 00431 * line prog.rb:11 false 00432 * c-call prog.rb:11 new Class 00433 * c-call prog.rb:11 initialize Object 00434 * c-return prog.rb:11 initialize Object 00435 * c-return prog.rb:11 new Class 00436 * line prog.rb:12 false 00437 * call prog.rb:2 test Test 00438 * line prog.rb:3 test Test 00439 * line prog.rb:4 test Test 00440 * return prog.rb:4 test Test 00441 */ 00442 00443 static VALUE 00444 set_trace_func(VALUE obj, VALUE trace) 00445 { 00446 rb_secure(4); 00447 00448 rb_remove_event_hook(call_trace_func); 00449 00450 if (NIL_P(trace)) { 00451 return Qnil; 00452 } 00453 00454 if (!rb_obj_is_proc(trace)) { 00455 rb_raise(rb_eTypeError, "trace_func needs to be Proc"); 00456 } 00457 00458 rb_add_event_hook(call_trace_func, RUBY_EVENT_ALL, trace); 00459 return trace; 00460 } 00461 00462 static void 00463 thread_add_trace_func(rb_thread_t *th, VALUE trace) 00464 { 00465 if (!rb_obj_is_proc(trace)) { 00466 rb_raise(rb_eTypeError, "trace_func needs to be Proc"); 00467 } 00468 00469 rb_threadptr_add_event_hook(th, call_trace_func, RUBY_EVENT_ALL, trace, RUBY_EVENT_HOOK_FLAG_SAFE); 00470 } 00471 00472 /* 00473 * call-seq: 00474 * thr.add_trace_func(proc) -> proc 00475 * 00476 * Adds _proc_ as a handler for tracing. 00477 * See <code>Thread#set_trace_func</code> and +set_trace_func+. 00478 */ 00479 00480 static VALUE 00481 thread_add_trace_func_m(VALUE obj, VALUE trace) 00482 { 00483 rb_thread_t *th; 00484 00485 rb_secure(4); 00486 GetThreadPtr(obj, th); 00487 thread_add_trace_func(th, trace); 00488 return trace; 00489 } 00490 00491 /* 00492 * call-seq: 00493 * thr.set_trace_func(proc) -> proc 00494 * thr.set_trace_func(nil) -> nil 00495 * 00496 * Establishes _proc_ on _thr_ as the handler for tracing, or 00497 * disables tracing if the parameter is +nil+. 00498 * See +set_trace_func+. 00499 */ 00500 00501 static VALUE 00502 thread_set_trace_func_m(VALUE obj, VALUE trace) 00503 { 00504 rb_thread_t *th; 00505 00506 rb_secure(4); 00507 GetThreadPtr(obj, th); 00508 rb_threadptr_remove_event_hook(th, call_trace_func, Qundef); 00509 00510 if (NIL_P(trace)) { 00511 return Qnil; 00512 } 00513 00514 thread_add_trace_func(th, trace); 00515 return trace; 00516 } 00517 00518 static const char * 00519 get_event_name(rb_event_flag_t event) 00520 { 00521 switch (event) { 00522 case RUBY_EVENT_LINE: return "line"; 00523 case RUBY_EVENT_CLASS: return "class"; 00524 case RUBY_EVENT_END: return "end"; 00525 case RUBY_EVENT_CALL: return "call"; 00526 case RUBY_EVENT_RETURN: return "return"; 00527 case RUBY_EVENT_C_CALL: return "c-call"; 00528 case RUBY_EVENT_C_RETURN: return "c-return"; 00529 case RUBY_EVENT_RAISE: return "raise"; 00530 default: 00531 return "unknown"; 00532 } 00533 } 00534 00535 static ID 00536 get_event_id(rb_event_flag_t event) 00537 { 00538 ID id; 00539 00540 switch (event) { 00541 #define C(name, NAME) case RUBY_EVENT_##NAME: CONST_ID(id, #name); return id; 00542 C(line, LINE); 00543 C(class, CLASS); 00544 C(end, END); 00545 C(call, CALL); 00546 C(return, RETURN); 00547 C(c_call, C_CALL); 00548 C(c_return, C_RETURN); 00549 C(raise, RAISE); 00550 C(b_call, B_CALL); 00551 C(b_return, B_RETURN); 00552 C(thread_begin, THREAD_BEGIN); 00553 C(thread_end, THREAD_END); 00554 C(specified_line, SPECIFIED_LINE); 00555 case RUBY_EVENT_LINE | RUBY_EVENT_SPECIFIED_LINE: CONST_ID(id, "line"); return id; 00556 #undef C 00557 default: 00558 return 0; 00559 } 00560 } 00561 00562 static void 00563 call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass) 00564 { 00565 const char *srcfile = rb_sourcefile(); 00566 VALUE eventname = rb_str_new2(get_event_name(event)); 00567 VALUE filename = srcfile ? rb_str_new2(srcfile) : Qnil; 00568 VALUE argv[6]; 00569 int line = rb_sourceline(); 00570 rb_thread_t *th = GET_THREAD(); 00571 00572 if (!klass) { 00573 rb_thread_method_id_and_class(th, &id, &klass); 00574 } 00575 00576 if (klass) { 00577 if (RB_TYPE_P(klass, T_ICLASS)) { 00578 klass = RBASIC(klass)->klass; 00579 } 00580 else if (FL_TEST(klass, FL_SINGLETON)) { 00581 klass = rb_iv_get(klass, "__attached__"); 00582 } 00583 } 00584 00585 argv[0] = eventname; 00586 argv[1] = filename; 00587 argv[2] = INT2FIX(line); 00588 argv[3] = id ? ID2SYM(id) : Qnil; 00589 argv[4] = (self && srcfile) ? rb_binding_new() : Qnil; 00590 argv[5] = klass ? klass : Qnil; 00591 00592 rb_proc_call_with_block(proc, 6, argv, Qnil); 00593 } 00594 00595 /* (2-2) TracePoint API */ 00596 00597 static VALUE rb_cTracePoint; 00598 00599 typedef struct rb_tp_struct { 00600 rb_event_flag_t events; 00601 rb_thread_t *target_th; 00602 void (*func)(VALUE tpval, void *data); 00603 void *data; 00604 VALUE proc; 00605 int tracing; 00606 VALUE self; 00607 } rb_tp_t; 00608 00609 static void 00610 tp_mark(void *ptr) 00611 { 00612 if (ptr) { 00613 rb_tp_t *tp = (rb_tp_t *)ptr; 00614 rb_gc_mark(tp->proc); 00615 if (tp->target_th) rb_gc_mark(tp->target_th->self); 00616 } 00617 } 00618 00619 static void 00620 tp_free(void *ptr) 00621 { 00622 /* do nothing */ 00623 } 00624 00625 static size_t 00626 tp_memsize(const void *ptr) 00627 { 00628 return sizeof(rb_tp_t); 00629 } 00630 00631 static const rb_data_type_t tp_data_type = { 00632 "tracepoint", 00633 {tp_mark, tp_free, tp_memsize,}, 00634 }; 00635 00636 static VALUE 00637 tp_alloc(VALUE klass) 00638 { 00639 rb_tp_t *tp; 00640 return TypedData_Make_Struct(klass, rb_tp_t, &tp_data_type, tp); 00641 } 00642 00643 static rb_event_flag_t 00644 symbol2event_flag(VALUE v) 00645 { 00646 static ID id; 00647 VALUE sym = rb_convert_type(v, T_SYMBOL, "Symbol", "to_sym"); 00648 00649 #define C(name, NAME) CONST_ID(id, #name); if (sym == ID2SYM(id)) return RUBY_EVENT_##NAME 00650 C(line, LINE); 00651 C(class, CLASS); 00652 C(end, END); 00653 C(call, CALL); 00654 C(return, RETURN); 00655 C(c_call, C_CALL); 00656 C(c_return, C_RETURN); 00657 C(raise, RAISE); 00658 C(b_call, B_CALL); 00659 C(b_return, B_RETURN); 00660 C(thread_begin, THREAD_BEGIN); 00661 C(thread_end, THREAD_END); 00662 C(specified_line, SPECIFIED_LINE); 00663 #undef C 00664 rb_raise(rb_eArgError, "unknown event: %s", rb_id2name(SYM2ID(sym))); 00665 } 00666 00667 static rb_tp_t * 00668 tpptr(VALUE tpval) 00669 { 00670 rb_tp_t *tp; 00671 TypedData_Get_Struct(tpval, rb_tp_t, &tp_data_type, tp); 00672 return tp; 00673 } 00674 00675 static rb_trace_arg_t * 00676 get_trace_arg(void) 00677 { 00678 rb_trace_arg_t *trace_arg = GET_THREAD()->trace_arg; 00679 if (trace_arg == 0) { 00680 rb_raise(rb_eRuntimeError, "access from outside"); 00681 } 00682 return trace_arg; 00683 } 00684 00685 struct rb_trace_arg_struct * 00686 rb_tracearg_from_tracepoint(VALUE tpval) 00687 { 00688 return get_trace_arg(); 00689 } 00690 00691 VALUE 00692 rb_tracearg_event(rb_trace_arg_t *trace_arg) 00693 { 00694 return ID2SYM(get_event_id(trace_arg->event)); 00695 } 00696 00697 static void 00698 fill_path_and_lineno(rb_trace_arg_t *trace_arg) 00699 { 00700 if (trace_arg->path == Qundef) { 00701 rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(trace_arg->th, trace_arg->cfp); 00702 00703 if (cfp) { 00704 trace_arg->path = cfp->iseq->location.path; 00705 trace_arg->lineno = rb_vm_get_sourceline(cfp); 00706 } 00707 else { 00708 trace_arg->path = Qnil; 00709 trace_arg->lineno = 0; 00710 } 00711 } 00712 } 00713 00714 VALUE 00715 rb_tracearg_lineno(rb_trace_arg_t *trace_arg) 00716 { 00717 fill_path_and_lineno(trace_arg); 00718 return INT2FIX(trace_arg->lineno); 00719 } 00720 VALUE 00721 rb_tracearg_path(rb_trace_arg_t *trace_arg) 00722 { 00723 fill_path_and_lineno(trace_arg); 00724 return trace_arg->path; 00725 } 00726 00727 static void 00728 fill_id_and_klass(rb_trace_arg_t *trace_arg) 00729 { 00730 if (!trace_arg->klass_solved) { 00731 if (!trace_arg->klass) { 00732 rb_vm_control_frame_id_and_class(trace_arg->cfp, &trace_arg->id, &trace_arg->klass); 00733 } 00734 00735 if (trace_arg->klass) { 00736 if (RB_TYPE_P(trace_arg->klass, T_ICLASS)) { 00737 trace_arg->klass = RBASIC(trace_arg->klass)->klass; 00738 } 00739 } 00740 else { 00741 trace_arg->klass = Qnil; 00742 } 00743 00744 trace_arg->klass_solved = 1; 00745 } 00746 } 00747 00748 VALUE 00749 rb_tracearg_method_id(rb_trace_arg_t *trace_arg) 00750 { 00751 fill_id_and_klass(trace_arg); 00752 return trace_arg->id ? ID2SYM(trace_arg->id) : Qnil; 00753 } 00754 00755 VALUE 00756 rb_tracearg_defined_class(rb_trace_arg_t *trace_arg) 00757 { 00758 fill_id_and_klass(trace_arg); 00759 return trace_arg->klass; 00760 } 00761 00762 VALUE 00763 rb_tracearg_binding(rb_trace_arg_t *trace_arg) 00764 { 00765 rb_control_frame_t *cfp; 00766 cfp = rb_vm_get_binding_creatable_next_cfp(trace_arg->th, trace_arg->cfp); 00767 00768 if (cfp) { 00769 return rb_binding_new_with_cfp(trace_arg->th, cfp); 00770 } 00771 else { 00772 return Qnil; 00773 } 00774 } 00775 00776 VALUE 00777 rb_tracearg_self(rb_trace_arg_t *trace_arg) 00778 { 00779 return trace_arg->self; 00780 } 00781 00782 VALUE 00783 rb_tracearg_return_value(rb_trace_arg_t *trace_arg) 00784 { 00785 if (trace_arg->event & (RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN | RUBY_EVENT_B_RETURN)) { 00786 /* ok */ 00787 } 00788 else { 00789 rb_raise(rb_eRuntimeError, "not supported by this event"); 00790 } 00791 if (trace_arg->data == Qundef) { 00792 rb_bug("tp_attr_return_value_m: unreachable"); 00793 } 00794 return trace_arg->data; 00795 } 00796 00797 VALUE 00798 rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg) 00799 { 00800 if (trace_arg->event & (RUBY_EVENT_RAISE)) { 00801 /* ok */ 00802 } 00803 else { 00804 rb_raise(rb_eRuntimeError, "not supported by this event"); 00805 } 00806 if (trace_arg->data == Qundef) { 00807 rb_bug("tp_attr_raised_exception_m: unreachable"); 00808 } 00809 return trace_arg->data; 00810 } 00811 00812 /* 00813 * Type of event 00814 * 00815 * See TracePoint@Events for more information. 00816 */ 00817 static VALUE 00818 tracepoint_attr_event(VALUE tpval) 00819 { 00820 return rb_tracearg_event(get_trace_arg()); 00821 } 00822 00823 /* 00824 * Line number of the event 00825 */ 00826 static VALUE 00827 tracepoint_attr_lineno(VALUE tpval) 00828 { 00829 return rb_tracearg_lineno(get_trace_arg()); 00830 } 00831 00832 /* 00833 * Path of the file being run 00834 */ 00835 static VALUE 00836 tracepoint_attr_path(VALUE tpval) 00837 { 00838 return rb_tracearg_path(get_trace_arg()); 00839 } 00840 00841 /* 00842 * Return the name of the method being called 00843 */ 00844 static VALUE 00845 tracepoint_attr_method_id(VALUE tpval) 00846 { 00847 return rb_tracearg_method_id(get_trace_arg()); 00848 } 00849 00850 /* 00851 * Return class or module of the method being called. 00852 * 00853 * class C; def foo; end; end 00854 * trace = TracePoint.new(:call) do |tp| 00855 * p tp.defined_class #=> C 00856 * end.enable do 00857 * C.new.foo 00858 * end 00859 * 00860 * If method is defined by a module, then that module is returned. 00861 * 00862 * module M; def foo; end; end 00863 * class C; include M; end; 00864 * trace = TracePoint.new(:call) do |tp| 00865 * p tp.defined_class #=> M 00866 * end.enable do 00867 * C.new.foo 00868 * end 00869 * 00870 * <b>Note:</b> #defined_class returns singleton class. 00871 * 00872 * 6th block parameter of Kernel#set_trace_func passes original class 00873 * of attached by singleton class. 00874 * 00875 * <b>This is a difference between Kernel#set_trace_func and TracePoint.</b> 00876 * 00877 * class C; def self.foo; end; end 00878 * trace = TracePoint.new(:call) do |tp| 00879 * p tp.defined_class #=> #<Class:C> 00880 * end.enable do 00881 * C.foo 00882 * end 00883 */ 00884 static VALUE 00885 tracepoint_attr_defined_class(VALUE tpval) 00886 { 00887 return rb_tracearg_defined_class(get_trace_arg()); 00888 } 00889 00890 /* 00891 * Return the generated binding object from event 00892 */ 00893 static VALUE 00894 tracepoint_attr_binding(VALUE tpval) 00895 { 00896 return rb_tracearg_binding(get_trace_arg()); 00897 } 00898 00899 /* 00900 * Return the trace object during event 00901 * 00902 * Same as TracePoint#binding: 00903 * trace.binding.eval('self') 00904 */ 00905 static VALUE 00906 tracepoint_attr_self(VALUE tpval) 00907 { 00908 return rb_tracearg_self(get_trace_arg()); 00909 } 00910 00911 /* 00912 * Return value from +:return+, +c_return+, and +b_return+ event 00913 */ 00914 static VALUE 00915 tracepoint_attr_return_value(VALUE tpval) 00916 { 00917 return rb_tracearg_return_value(get_trace_arg()); 00918 } 00919 00920 /* 00921 * Value from exception raised on the +:raise+ event 00922 */ 00923 static VALUE 00924 tracepoint_attr_raised_exception(VALUE tpval) 00925 { 00926 return rb_tracearg_raised_exception(get_trace_arg()); 00927 } 00928 00929 static void 00930 tp_call_trace(VALUE tpval, rb_trace_arg_t *trace_arg) 00931 { 00932 rb_tp_t *tp = tpptr(tpval); 00933 00934 if (tp->func) { 00935 (*tp->func)(tpval, tp->data); 00936 } 00937 else { 00938 rb_proc_call_with_block((VALUE)tp->proc, 1, &tpval, Qnil); 00939 } 00940 } 00941 00942 VALUE 00943 rb_tracepoint_enable(VALUE tpval) 00944 { 00945 rb_tp_t *tp; 00946 00947 rb_secure(4); 00948 tp = tpptr(tpval); 00949 00950 if (tp->target_th) { 00951 rb_thread_add_event_hook2(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tp->events, tpval, 00952 RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG); 00953 } 00954 else { 00955 rb_add_event_hook2((rb_event_hook_func_t)tp_call_trace, tp->events, tpval, 00956 RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG); 00957 } 00958 tp->tracing = 1; 00959 return Qundef; 00960 } 00961 00962 VALUE 00963 rb_tracepoint_disable(VALUE tpval) 00964 { 00965 rb_tp_t *tp; 00966 00967 rb_secure(4); 00968 tp = tpptr(tpval); 00969 00970 if (tp->target_th) { 00971 rb_thread_remove_event_hook_with_data(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tpval); 00972 } 00973 else { 00974 rb_remove_event_hook_with_data((rb_event_hook_func_t)tp_call_trace, tpval); 00975 } 00976 tp->tracing = 0; 00977 return Qundef; 00978 } 00979 00980 /* 00981 * call-seq: 00982 * trace.enable -> true or false 00983 * trace.enable { block } -> obj 00984 * 00985 * Activates the trace 00986 * 00987 * Return true if trace was enabled. 00988 * Return false if trace was disabled. 00989 * 00990 * trace.enabled? #=> false 00991 * trace.enable #=> false (previous state) 00992 * # trace is enabled 00993 * trace.enabled? #=> true 00994 * trace.enable #=> true (previous state) 00995 * # trace is still enabled 00996 * 00997 * If a block is given, the trace will only be enabled within the scope of the 00998 * block. 00999 * 01000 * trace.enabled? 01001 * #=> false 01002 * 01003 * trace.enable do 01004 * trace.enabled? 01005 * # only enabled for this block 01006 * end 01007 * 01008 * trace.enabled? 01009 * #=> false 01010 * 01011 * Note: You cannot access event hooks within the block. 01012 * 01013 * trace.enable { p tp.lineno } 01014 * #=> RuntimeError: access from outside 01015 * 01016 */ 01017 static VALUE 01018 tracepoint_enable_m(VALUE tpval) 01019 { 01020 rb_tp_t *tp = tpptr(tpval); 01021 int previous_tracing = tp->tracing; 01022 rb_tracepoint_enable(tpval); 01023 01024 if (rb_block_given_p()) { 01025 return rb_ensure(rb_yield, Qnil, 01026 previous_tracing ? rb_tracepoint_enable : rb_tracepoint_disable, 01027 tpval); 01028 } 01029 else { 01030 return previous_tracing ? Qtrue : Qfalse; 01031 } 01032 } 01033 01034 /* 01035 * call-seq: 01036 * trace.disable -> true or false 01037 * trace.disable { block } -> obj 01038 * 01039 * Deactivates the trace 01040 * 01041 * Return true if trace was enabled. 01042 * Return false if trace was disabled. 01043 * 01044 * trace.enabled? #=> true 01045 * trace.disable #=> false (previous status) 01046 * trace.enabled? #=> false 01047 * trace.disable #=> false 01048 * 01049 * If a block is given, the trace will only be disable within the scope of the 01050 * block. 01051 * 01052 * trace.enabled? 01053 * #=> true 01054 * 01055 * trace.disable do 01056 * trace.enabled? 01057 * # only disabled for this block 01058 * end 01059 * 01060 * trace.enabled? 01061 * #=> true 01062 * 01063 * Note: You cannot access event hooks within the block. 01064 * 01065 * trace.disable { p tp.lineno } 01066 * #=> RuntimeError: access from outside 01067 */ 01068 static VALUE 01069 tracepoint_disable_m(VALUE tpval) 01070 { 01071 rb_tp_t *tp = tpptr(tpval); 01072 int previous_tracing = tp->tracing; 01073 rb_tracepoint_disable(tpval); 01074 01075 if (rb_block_given_p()) { 01076 return rb_ensure(rb_yield, Qnil, 01077 previous_tracing ? rb_tracepoint_enable : rb_tracepoint_disable, 01078 tpval); 01079 } 01080 else { 01081 return previous_tracing ? Qtrue : Qfalse; 01082 } 01083 } 01084 01085 /* 01086 * call-seq: 01087 * trace.enabled? -> true or false 01088 * 01089 * The current status of the trace 01090 */ 01091 VALUE 01092 rb_tracepoint_enabled_p(VALUE tpval) 01093 { 01094 rb_tp_t *tp = tpptr(tpval); 01095 return tp->tracing ? Qtrue : Qfalse; 01096 } 01097 01098 static VALUE 01099 tracepoint_new(VALUE klass, rb_thread_t *target_th, rb_event_flag_t events, void (func)(VALUE, void*), void *data, VALUE proc) 01100 { 01101 VALUE tpval = tp_alloc(klass); 01102 rb_tp_t *tp; 01103 TypedData_Get_Struct(tpval, rb_tp_t, &tp_data_type, tp); 01104 01105 tp->proc = proc; 01106 tp->func = func; 01107 tp->data = data; 01108 tp->events = events; 01109 tp->self = tpval; 01110 01111 return tpval; 01112 } 01113 01114 VALUE 01115 rb_tracepoint_new(VALUE target_thval, rb_event_flag_t events, void (*func)(VALUE, void *), void *data) 01116 { 01117 rb_thread_t *target_th = 0; 01118 if (RTEST(target_thval)) { 01119 GetThreadPtr(target_thval, target_th); 01120 /* TODO: Test it! 01121 * Warning: This function is not tested. 01122 */ 01123 } 01124 return tracepoint_new(rb_cTracePoint, target_th, events, func, data, Qundef); 01125 } 01126 01127 /* 01128 * call-seq: 01129 * TracePoint.new(*events) { |obj| block } -> obj 01130 * 01131 * Returns a new TracePoint object, not enabled by default. 01132 * 01133 * Next, in order to activate the trace, you must use TracePoint.enable 01134 * 01135 * trace = TracePoint.new(:call) do |tp| 01136 * p [tp.lineno, tp.defined_class, tp.method_id, tp.event] 01137 * end 01138 * #=> #<TracePoint:0x007f17372cdb20> 01139 * 01140 * trace.enable 01141 * #=> #<TracePoint:0x007f17372cdb20> 01142 * 01143 * puts "Hello, TracePoint!" 01144 * # ... 01145 * # [48, IRB::Notifier::AbstractNotifier, :printf, :call] 01146 * # ... 01147 * 01148 * When you want to deactivate the trace, you must use TracePoint.disable 01149 * 01150 * trace.disable 01151 * 01152 * See TracePoint@Events for possible events and more information. 01153 * 01154 * A block must be given, otherwise a ThreadError is raised. 01155 * 01156 * If the trace method isn't included in the given events filter, a 01157 * RuntimeError is raised. 01158 * 01159 * TracePoint.trace(:line) do |tp| 01160 * p tp.raised_exception 01161 * end 01162 * #=> RuntimeError: 'raised_exception' not supported by this event 01163 * 01164 * If the trace method is called outside block, a RuntimeError is raised. 01165 * 01166 * TracePoint.trace(:line) do |tp| 01167 * $tp = tp 01168 * end 01169 * $tp.line #=> access from outside (RuntimeError) 01170 * 01171 * Access from other threads is also forbidden. 01172 * 01173 */ 01174 static VALUE 01175 tracepoint_new_s(int argc, VALUE *argv, VALUE self) 01176 { 01177 rb_event_flag_t events = 0; 01178 int i; 01179 01180 if (argc > 0) { 01181 for (i=0; i<argc; i++) { 01182 events |= symbol2event_flag(argv[i]); 01183 } 01184 } 01185 else { 01186 events = RUBY_EVENT_TRACEPOINT_ALL; 01187 } 01188 01189 if (!rb_block_given_p()) { 01190 rb_raise(rb_eThreadError, "must be called with a block"); 01191 } 01192 01193 return tracepoint_new(self, 0, events, 0, 0, rb_block_proc()); 01194 } 01195 01196 static VALUE 01197 tracepoint_trace_s(int argc, VALUE *argv, VALUE self) 01198 { 01199 VALUE trace = tracepoint_new_s(argc, argv, self); 01200 rb_tracepoint_enable(trace); 01201 return trace; 01202 } 01203 01204 /* 01205 * call-seq: 01206 * trace.inspect -> string 01207 * 01208 * Return a string containing a human-readable TracePoint 01209 * status. 01210 */ 01211 01212 static VALUE 01213 tracepoint_inspect(VALUE self) 01214 { 01215 rb_tp_t *tp = tpptr(self); 01216 rb_trace_arg_t *trace_arg = GET_THREAD()->trace_arg; 01217 01218 if (trace_arg) { 01219 switch (trace_arg->event) { 01220 case RUBY_EVENT_LINE: 01221 case RUBY_EVENT_SPECIFIED_LINE: 01222 { 01223 VALUE sym = rb_tracearg_method_id(trace_arg); 01224 if (NIL_P(sym)) 01225 goto default_inspect; 01226 return rb_sprintf("#<TracePoint:%"PRIsVALUE"@%"PRIsVALUE":%d in `%"PRIsVALUE"'>", 01227 rb_tracearg_event(trace_arg), 01228 rb_tracearg_path(trace_arg), 01229 FIX2INT(rb_tracearg_lineno(trace_arg)), 01230 sym); 01231 } 01232 case RUBY_EVENT_CALL: 01233 case RUBY_EVENT_C_CALL: 01234 case RUBY_EVENT_RETURN: 01235 case RUBY_EVENT_C_RETURN: 01236 return rb_sprintf("#<TracePoint:%"PRIsVALUE" `%"PRIsVALUE"'@%"PRIsVALUE":%d>", 01237 rb_tracearg_event(trace_arg), 01238 rb_tracearg_method_id(trace_arg), 01239 rb_tracearg_path(trace_arg), 01240 FIX2INT(rb_tracearg_lineno(trace_arg))); 01241 case RUBY_EVENT_THREAD_BEGIN: 01242 case RUBY_EVENT_THREAD_END: 01243 return rb_sprintf("#<TracePoint:%"PRIsVALUE" %"PRIsVALUE">", 01244 rb_tracearg_event(trace_arg), 01245 rb_tracearg_self(trace_arg)); 01246 default: 01247 default_inspect: 01248 return rb_sprintf("#<TracePoint:%"PRIsVALUE"@%"PRIsVALUE":%d>", 01249 rb_tracearg_event(trace_arg), 01250 rb_tracearg_path(trace_arg), 01251 FIX2INT(rb_tracearg_lineno(trace_arg))); 01252 } 01253 } 01254 else { 01255 return rb_sprintf("#<TracePoint:%s>", tp->tracing ? "enabled" : "disabled"); 01256 } 01257 } 01258 01259 /* This function is called from inits.c */ 01260 void 01261 Init_vm_trace(void) 01262 { 01263 /* trace_func */ 01264 rb_define_global_function("set_trace_func", set_trace_func, 1); 01265 rb_define_method(rb_cThread, "set_trace_func", thread_set_trace_func_m, 1); 01266 rb_define_method(rb_cThread, "add_trace_func", thread_add_trace_func_m, 1); 01267 01268 /* 01269 * Document-class: TracePoint 01270 * 01271 * A class that provides the functionality of Kernel#set_trace_func in a 01272 * nice Object-Oriented API. 01273 * 01274 * == Example 01275 * 01276 * We can use TracePoint to gather information specifically for exceptions: 01277 * 01278 * trace = TracePoint.new(:raise) do |tp| 01279 * p [tp.lineno, tp.event, tp.raised_exception] 01280 * end 01281 * #=> #<TracePoint:0x007f786a452448> 01282 * 01283 * trace.enable 01284 * #=> #<TracePoint:0x007f786a452448> 01285 * 01286 * 0 / 0 01287 * #=> [5, :raise, #<ZeroDivisionError: divided by 0>] 01288 * 01289 * == Events 01290 * 01291 * If you don't specify the type of events you want to listen for, 01292 * TracePoint will include all available events. 01293 * 01294 * *Note* do not depend on current event set, as this list is subject to 01295 * change. Instead, it is recommended you specify the type of events you 01296 * want to use. 01297 * 01298 * To filter what is traced, you can pass any of the following as +events+: 01299 * 01300 * +:line+:: execute code on a new line 01301 * +:class+:: start a class or module definition 01302 * +:end+:: finish a class or module definition 01303 * +:call+:: call a Ruby method 01304 * +:return+:: return from a Ruby method 01305 * +:c_call+:: call a C-language routine 01306 * +:c_return+:: return from a C-language routine 01307 * +:raise+:: raise an exception 01308 * +:b_call+:: event hook at block entry 01309 * +:b_return+:: event hook at block ending 01310 * +:thread_begin+:: event hook at thread beginning 01311 * +:thread_end+:: event hook at thread ending 01312 * 01313 */ 01314 rb_cTracePoint = rb_define_class("TracePoint", rb_cObject); 01315 rb_undef_alloc_func(rb_cTracePoint); 01316 rb_undef_method(CLASS_OF(rb_cTracePoint), "new"); 01317 rb_define_singleton_method(rb_cTracePoint, "new", tracepoint_new_s, -1); 01318 /* 01319 * Document-method: trace 01320 * 01321 * call-seq: 01322 * TracePoint.trace(*events) { |obj| block } -> obj 01323 * 01324 * A convenience method for TracePoint.new, that activates the trace 01325 * automatically. 01326 * 01327 * trace = TracePoint.trace(:call) { |tp| [tp.lineno, tp.event] } 01328 * #=> #<TracePoint:0x007f786a452448> 01329 * 01330 * trace.enabled? #=> true 01331 */ 01332 rb_define_singleton_method(rb_cTracePoint, "trace", tracepoint_trace_s, -1); 01333 01334 rb_define_method(rb_cTracePoint, "enable", tracepoint_enable_m, 0); 01335 rb_define_method(rb_cTracePoint, "disable", tracepoint_disable_m, 0); 01336 rb_define_method(rb_cTracePoint, "enabled?", rb_tracepoint_enabled_p, 0); 01337 01338 rb_define_method(rb_cTracePoint, "inspect", tracepoint_inspect, 0); 01339 01340 rb_define_method(rb_cTracePoint, "event", tracepoint_attr_event, 0); 01341 rb_define_method(rb_cTracePoint, "lineno", tracepoint_attr_lineno, 0); 01342 rb_define_method(rb_cTracePoint, "path", tracepoint_attr_path, 0); 01343 rb_define_method(rb_cTracePoint, "method_id", tracepoint_attr_method_id, 0); 01344 rb_define_method(rb_cTracePoint, "defined_class", tracepoint_attr_defined_class, 0); 01345 rb_define_method(rb_cTracePoint, "binding", tracepoint_attr_binding, 0); 01346 rb_define_method(rb_cTracePoint, "self", tracepoint_attr_self, 0); 01347 rb_define_method(rb_cTracePoint, "return_value", tracepoint_attr_return_value, 0); 01348 rb_define_method(rb_cTracePoint, "raised_exception", tracepoint_attr_raised_exception, 0); 01349 } 01350 01351