Ruby  2.0.0p481(2014-05-08revision45883)
vm_trace.c
Go to the documentation of this file.
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