Ruby  2.0.0p247(2013-06-27revision41674)
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 
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