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