1/**********************************************************************
2
3  vm_trace.c -
4
5  $Author: ko1 $
6  created at: Tue Aug 14 19:37:09 2012
7
8  Copyright (C) 1993-2012 Yukihiro Matsumoto
9
10**********************************************************************/
11
12/*
13 * This file incldue two parts:
14 *
15 * (1) set_trace_func internal mechanisms
16 *     and C level API
17 *
18 * (2) Ruby level API
19 *  (2-1) set_trace_func API
20 *  (2-2) TracePoint API (not yet)
21 *
22 */
23
24#include "ruby/ruby.h"
25#include "ruby/debug.h"
26#include "ruby/encoding.h"
27
28#include "internal.h"
29#include "vm_core.h"
30#include "eval_intern.h"
31
32/* (1) trace mechanisms */
33
34typedef struct rb_event_hook_struct {
35    rb_event_hook_flag_t hook_flags;
36    rb_event_flag_t events;
37    rb_event_hook_func_t func;
38    VALUE data;
39    struct rb_event_hook_struct *next;
40} rb_event_hook_t;
41
42typedef void (*rb_event_hook_raw_arg_func_t)(VALUE data, const rb_trace_arg_t *arg);
43
44#define MAX_EVENT_NUM 32
45
46static int ruby_event_flag_count[MAX_EVENT_NUM] = {0};
47
48/* called from vm.c */
49
50void
51vm_trace_mark_event_hooks(rb_hook_list_t *hooks)
52{
53    rb_event_hook_t *hook = hooks->hooks;
54
55    while (hook) {
56	rb_gc_mark(hook->data);
57	hook = hook->next;
58    }
59}
60
61/* ruby_vm_event_flags management */
62
63static void
64recalc_add_ruby_vm_event_flags(rb_event_flag_t events)
65{
66    int i;
67    ruby_vm_event_flags = 0;
68
69    for (i=0; i<MAX_EVENT_NUM; i++) {
70	if (events & (1 << i)) {
71	    ruby_event_flag_count[i]++;
72	}
73	ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0;
74    }
75}
76
77static void
78recalc_remove_ruby_vm_event_flags(rb_event_flag_t events)
79{
80    int i;
81    ruby_vm_event_flags = 0;
82
83    for (i=0; i<MAX_EVENT_NUM; i++) {
84	if (events & (1 << i)) {
85	    ruby_event_flag_count[i]--;
86	}
87	ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0;
88    }
89}
90
91/* add/remove hooks */
92
93static rb_thread_t *
94thval2thread_t(VALUE thval)
95{
96    rb_thread_t *th;
97    GetThreadPtr(thval, th);
98    return th;
99}
100
101static rb_event_hook_t *
102alloc_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
103{
104    rb_event_hook_t *hook = ALLOC(rb_event_hook_t);
105    hook->hook_flags = hook_flags;
106    hook->events = events;
107    hook->func = func;
108    hook->data = data;
109    return hook;
110}
111
112static void
113connect_event_hook(rb_hook_list_t *list, rb_event_hook_t *hook)
114{
115    hook->next = list->hooks;
116    list->hooks = hook;
117    recalc_add_ruby_vm_event_flags(hook->events);
118    list->events |= hook->events;
119}
120
121static void
122rb_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)
123{
124    rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags);
125    connect_event_hook(&th->event_hooks, hook);
126}
127
128void
129rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
130{
131    rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data, RUBY_EVENT_HOOK_FLAG_SAFE);
132}
133
134void
135rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
136{
137    rb_event_hook_t *hook = alloc_event_hook(func, events, data, RUBY_EVENT_HOOK_FLAG_SAFE);
138    connect_event_hook(&GET_VM()->event_hooks, hook);
139}
140
141void
142rb_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)
143{
144    rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data, hook_flags);
145}
146
147void
148rb_add_event_hook2(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
149{
150    rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags);
151    connect_event_hook(&GET_VM()->event_hooks, hook);
152}
153
154/* if func is 0, then clear all funcs */
155static int
156remove_event_hook(rb_hook_list_t *list, rb_event_hook_func_t func, VALUE data)
157{
158    int ret = 0;
159    rb_event_hook_t *hook = list->hooks;
160
161    while (hook) {
162	if (func == 0 || hook->func == func) {
163	    if (data == Qundef || hook->data == data) {
164		hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED;
165		ret+=1;
166		list->need_clean++;
167	    }
168	}
169	hook = hook->next;
170    }
171
172    return ret;
173}
174
175static int
176rb_threadptr_remove_event_hook(rb_thread_t *th, rb_event_hook_func_t func, VALUE data)
177{
178    return remove_event_hook(&th->event_hooks, func, data);
179}
180
181int
182rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func)
183{
184    return rb_threadptr_remove_event_hook(thval2thread_t(thval), func, Qundef);
185}
186
187int
188rb_thread_remove_event_hook_with_data(VALUE thval, rb_event_hook_func_t func, VALUE data)
189{
190    return rb_threadptr_remove_event_hook(thval2thread_t(thval), func, data);
191}
192
193int
194rb_remove_event_hook(rb_event_hook_func_t func)
195{
196    return remove_event_hook(&GET_VM()->event_hooks, func, Qundef);
197}
198
199int
200rb_remove_event_hook_with_data(rb_event_hook_func_t func, VALUE data)
201{
202    return remove_event_hook(&GET_VM()->event_hooks, func, data);
203}
204
205static int
206clear_trace_func_i(st_data_t key, st_data_t val, st_data_t flag)
207{
208    rb_thread_t *th;
209    GetThreadPtr((VALUE)key, th);
210    rb_threadptr_remove_event_hook(th, 0, Qundef);
211    return ST_CONTINUE;
212}
213
214void
215rb_clear_trace_func(void)
216{
217    st_foreach(GET_VM()->living_threads, clear_trace_func_i, (st_data_t) 0);
218    rb_remove_event_hook(0);
219}
220
221/* invoke hooks */
222
223static void
224clean_hooks(rb_hook_list_t *list)
225{
226    rb_event_hook_t *hook, **nextp = &list->hooks;
227
228    list->events = 0;
229    list->need_clean = 0;
230
231    while ((hook = *nextp) != 0) {
232	if (hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) {
233	    *nextp = hook->next;
234	    recalc_remove_ruby_vm_event_flags(hook->events);
235	    xfree(hook);
236	}
237	else {
238	    list->events |= hook->events; /* update active events */
239	    nextp = &hook->next;
240	}
241    }
242}
243
244static int
245exec_hooks(rb_thread_t *th, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg, int can_clean_hooks)
246{
247    int state;
248    volatile int raised;
249
250    if (UNLIKELY(list->need_clean > 0) && can_clean_hooks) {
251	clean_hooks(list);
252    }
253
254    raised = rb_threadptr_reset_raised(th);
255
256    /* TODO: Support !RUBY_EVENT_HOOK_FLAG_SAFE hooks */
257
258    TH_PUSH_TAG(th);
259    if ((state = TH_EXEC_TAG()) == 0) {
260	rb_event_hook_t *hook;
261
262	for (hook = list->hooks; hook; hook = hook->next) {
263	    if (LIKELY(!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED)) && (trace_arg->event & hook->events)) {
264		if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_RAW_ARG)) {
265		    (*hook->func)(trace_arg->event, hook->data, trace_arg->self, trace_arg->id, trace_arg->klass);
266		}
267		else {
268		    (*((rb_event_hook_raw_arg_func_t)hook->func))(hook->data, trace_arg);
269		}
270	    }
271	}
272    }
273    TH_POP_TAG();
274
275    if (raised) {
276	rb_threadptr_set_raised(th);
277    }
278
279    return state;
280}
281
282static void
283rb_threadptr_exec_event_hooks_orig(rb_trace_arg_t *trace_arg, int pop_p)
284{
285    rb_thread_t *th = trace_arg->th;
286    if (th->trace_arg == 0 &&
287	trace_arg->self != rb_mRubyVMFrozenCore /* skip special methods. TODO: remove it. */) {
288	const int vm_tracing = th->vm->trace_running;
289	const VALUE errinfo = th->errinfo;
290	const int outer_state = th->state;
291	int state = 0;
292	th->state = 0;
293	th->errinfo = Qnil;
294
295	th->vm->trace_running++;
296	th->trace_arg = trace_arg;
297	{
298	    rb_hook_list_t *list;
299
300	    /* thread local traces */
301	    list = &th->event_hooks;
302	    if (list->events & trace_arg->event) {
303		state = exec_hooks(th, list, trace_arg, TRUE);
304		if (state) goto terminate;
305	    }
306
307	    /* vm global traces */
308	    list = &th->vm->event_hooks;
309	    if (list->events & trace_arg->event) {
310		state = exec_hooks(th, list, trace_arg, !vm_tracing);
311		if (state) goto terminate;
312	    }
313	    th->errinfo = errinfo;
314	}
315      terminate:
316	th->trace_arg = 0;
317	th->vm->trace_running--;
318
319	if (state) {
320	    if (pop_p) {
321		if (VM_FRAME_TYPE_FINISH_P(th->cfp)) {
322		    th->tag = th->tag->prev;
323		}
324		th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
325	    }
326	    TH_JUMP_TAG(th, state);
327	}
328	th->state = outer_state;
329    }
330}
331
332void
333rb_threadptr_exec_event_hooks_and_pop_frame(rb_trace_arg_t *trace_arg)
334{
335    rb_threadptr_exec_event_hooks_orig(trace_arg, 1);
336}
337
338void
339rb_threadptr_exec_event_hooks(rb_trace_arg_t *trace_arg)
340{
341    rb_threadptr_exec_event_hooks_orig(trace_arg, 0);
342}
343
344VALUE
345rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg)
346{
347    volatile int raised;
348    volatile int outer_state;
349    VALUE result = Qnil;
350    rb_thread_t *th = GET_THREAD();
351    int state;
352    const int tracing = th->trace_arg ? 1 : 0;
353    rb_trace_arg_t dummy_trace_arg;
354
355    if (!tracing) th->vm->trace_running++;
356    if (!th->trace_arg) th->trace_arg = &dummy_trace_arg;
357
358    raised = rb_threadptr_reset_raised(th);
359    outer_state = th->state;
360    th->state = 0;
361
362    TH_PUSH_TAG(th);
363    if ((state = TH_EXEC_TAG()) == 0) {
364	result = (*func)(arg);
365    }
366    TH_POP_TAG();
367
368    if (raised) {
369	rb_threadptr_set_raised(th);
370    }
371
372    if (th->trace_arg == &dummy_trace_arg) th->trace_arg = 0;
373    if (!tracing) th->vm->trace_running--;
374
375    if (state) {
376	JUMP_TAG(state);
377    }
378
379    th->state = outer_state;
380    return result;
381}
382
383static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALUE klass);
384
385/* (2-1) set_trace_func (old API) */
386
387/*
388 *  call-seq:
389 *     set_trace_func(proc)    -> proc
390 *     set_trace_func(nil)     -> nil
391 *
392 *  Establishes _proc_ as the handler for tracing, or disables
393 *  tracing if the parameter is +nil+.
394 *
395 *  _proc_ takes up to six parameters:
396 *
397 *  *	an event name
398 *  *	a filename
399 *  *	a line number
400 *  *	an object id
401 *  *	a binding
402 *  *	the name of a class
403 *
404 *  _proc_ is invoked whenever an event occurs.
405 *
406 *  Events are:
407 *
408 *  +c-call+:: call a C-language routine
409 *  +c-return+:: return from a C-language routine
410 *  +call+:: call a Ruby method
411 *  +class+:: start a class or module definition),
412 *  +end+:: finish a class or module definition),
413 *  +line+:: execute code on a new line
414 *  +raise+:: raise an exception
415 *  +return+:: return from a Ruby method
416 *
417 *  Tracing is disabled within the context of _proc_.
418 *
419 *      class Test
420 *	def test
421 *	  a = 1
422 *	  b = 2
423 *	end
424 *      end
425 *
426 *      set_trace_func proc { |event, file, line, id, binding, classname|
427 *	   printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
428 *      }
429 *      t = Test.new
430 *      t.test
431 *
432 *	  line prog.rb:11               false
433 *      c-call prog.rb:11        new    Class
434 *      c-call prog.rb:11 initialize   Object
435 *    c-return prog.rb:11 initialize   Object
436 *    c-return prog.rb:11        new    Class
437 *	  line prog.rb:12               false
438 *  	  call prog.rb:2        test     Test
439 *	  line prog.rb:3        test     Test
440 *	  line prog.rb:4        test     Test
441 *      return prog.rb:4        test     Test
442 */
443
444static VALUE
445set_trace_func(VALUE obj, VALUE trace)
446{
447    rb_secure(4);
448
449    rb_remove_event_hook(call_trace_func);
450
451    if (NIL_P(trace)) {
452	return Qnil;
453    }
454
455    if (!rb_obj_is_proc(trace)) {
456	rb_raise(rb_eTypeError, "trace_func needs to be Proc");
457    }
458
459    rb_add_event_hook(call_trace_func, RUBY_EVENT_ALL, trace);
460    return trace;
461}
462
463static void
464thread_add_trace_func(rb_thread_t *th, VALUE trace)
465{
466    if (!rb_obj_is_proc(trace)) {
467	rb_raise(rb_eTypeError, "trace_func needs to be Proc");
468    }
469
470    rb_threadptr_add_event_hook(th, call_trace_func, RUBY_EVENT_ALL, trace, RUBY_EVENT_HOOK_FLAG_SAFE);
471}
472
473/*
474 *  call-seq:
475 *     thr.add_trace_func(proc)    -> proc
476 *
477 *  Adds _proc_ as a handler for tracing.
478 *  See <code>Thread#set_trace_func</code> and +set_trace_func+.
479 */
480
481static VALUE
482thread_add_trace_func_m(VALUE obj, VALUE trace)
483{
484    rb_thread_t *th;
485
486    rb_secure(4);
487    GetThreadPtr(obj, th);
488    thread_add_trace_func(th, trace);
489    return trace;
490}
491
492/*
493 *  call-seq:
494 *     thr.set_trace_func(proc)    -> proc
495 *     thr.set_trace_func(nil)     -> nil
496 *
497 *  Establishes _proc_ on _thr_ as the handler for tracing, or
498 *  disables tracing if the parameter is +nil+.
499 *  See +set_trace_func+.
500 */
501
502static VALUE
503thread_set_trace_func_m(VALUE obj, VALUE trace)
504{
505    rb_thread_t *th;
506
507    rb_secure(4);
508    GetThreadPtr(obj, th);
509    rb_threadptr_remove_event_hook(th, call_trace_func, Qundef);
510
511    if (NIL_P(trace)) {
512	return Qnil;
513    }
514
515    thread_add_trace_func(th, trace);
516    return trace;
517}
518
519static const char *
520get_event_name(rb_event_flag_t event)
521{
522    switch (event) {
523      case RUBY_EVENT_LINE:     return "line";
524      case RUBY_EVENT_CLASS:    return "class";
525      case RUBY_EVENT_END:      return "end";
526      case RUBY_EVENT_CALL:     return "call";
527      case RUBY_EVENT_RETURN:	return "return";
528      case RUBY_EVENT_C_CALL:	return "c-call";
529      case RUBY_EVENT_C_RETURN:	return "c-return";
530      case RUBY_EVENT_RAISE:	return "raise";
531      default:
532	return "unknown";
533    }
534}
535
536static ID
537get_event_id(rb_event_flag_t event)
538{
539    ID id;
540
541    switch (event) {
542#define C(name, NAME) case RUBY_EVENT_##NAME: CONST_ID(id, #name); return id;
543	C(line, LINE);
544	C(class, CLASS);
545	C(end, END);
546	C(call, CALL);
547	C(return, RETURN);
548	C(c_call, C_CALL);
549	C(c_return, C_RETURN);
550	C(raise, RAISE);
551	C(b_call, B_CALL);
552	C(b_return, B_RETURN);
553	C(thread_begin, THREAD_BEGIN);
554	C(thread_end, THREAD_END);
555	C(specified_line, SPECIFIED_LINE);
556      case RUBY_EVENT_LINE | RUBY_EVENT_SPECIFIED_LINE: CONST_ID(id, "line"); return id;
557#undef C
558      default:
559	return 0;
560    }
561}
562
563static void
564call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
565{
566    const char *srcfile = rb_sourcefile();
567    VALUE eventname = rb_str_new2(get_event_name(event));
568    VALUE filename = srcfile ? rb_str_new2(srcfile) : Qnil;
569    VALUE argv[6];
570    int line = rb_sourceline();
571    rb_thread_t *th = GET_THREAD();
572
573    if (!klass) {
574	rb_thread_method_id_and_class(th, &id, &klass);
575    }
576
577    if (klass) {
578	if (RB_TYPE_P(klass, T_ICLASS)) {
579	    klass = RBASIC(klass)->klass;
580	}
581	else if (FL_TEST(klass, FL_SINGLETON)) {
582	    klass = rb_iv_get(klass, "__attached__");
583	}
584    }
585
586    argv[0] = eventname;
587    argv[1] = filename;
588    argv[2] = INT2FIX(line);
589    argv[3] = id ? ID2SYM(id) : Qnil;
590    argv[4] = (self && srcfile) ? rb_binding_new() : Qnil;
591    argv[5] = klass ? klass : Qnil;
592
593    rb_proc_call_with_block(proc, 6, argv, Qnil);
594}
595
596/* (2-2) TracePoint API */
597
598static VALUE rb_cTracePoint;
599
600typedef struct rb_tp_struct {
601    rb_event_flag_t events;
602    rb_thread_t *target_th;
603    void (*func)(VALUE tpval, void *data);
604    void *data;
605    VALUE proc;
606    int tracing;
607    VALUE self;
608} rb_tp_t;
609
610static void
611tp_mark(void *ptr)
612{
613    if (ptr) {
614	rb_tp_t *tp = (rb_tp_t *)ptr;
615	rb_gc_mark(tp->proc);
616	if (tp->target_th) rb_gc_mark(tp->target_th->self);
617    }
618}
619
620static void
621tp_free(void *ptr)
622{
623    /* do nothing */
624}
625
626static size_t
627tp_memsize(const void *ptr)
628{
629    return sizeof(rb_tp_t);
630}
631
632static const rb_data_type_t tp_data_type = {
633    "tracepoint",
634    {tp_mark, tp_free, tp_memsize,},
635};
636
637static VALUE
638tp_alloc(VALUE klass)
639{
640    rb_tp_t *tp;
641    return TypedData_Make_Struct(klass, rb_tp_t, &tp_data_type, tp);
642}
643
644static rb_event_flag_t
645symbol2event_flag(VALUE v)
646{
647    static ID id;
648    VALUE sym = rb_convert_type(v, T_SYMBOL, "Symbol", "to_sym");
649
650#define C(name, NAME) CONST_ID(id, #name); if (sym == ID2SYM(id)) return RUBY_EVENT_##NAME
651    C(line, LINE);
652    C(class, CLASS);
653    C(end, END);
654    C(call, CALL);
655    C(return, RETURN);
656    C(c_call, C_CALL);
657    C(c_return, C_RETURN);
658    C(raise, RAISE);
659    C(b_call, B_CALL);
660    C(b_return, B_RETURN);
661    C(thread_begin, THREAD_BEGIN);
662    C(thread_end, THREAD_END);
663    C(specified_line, SPECIFIED_LINE);
664#undef C
665    rb_raise(rb_eArgError, "unknown event: %s", rb_id2name(SYM2ID(sym)));
666}
667
668static rb_tp_t *
669tpptr(VALUE tpval)
670{
671    rb_tp_t *tp;
672    TypedData_Get_Struct(tpval, rb_tp_t, &tp_data_type, tp);
673    return tp;
674}
675
676static rb_trace_arg_t *
677get_trace_arg(void)
678{
679    rb_trace_arg_t *trace_arg = GET_THREAD()->trace_arg;
680    if (trace_arg == 0) {
681	rb_raise(rb_eRuntimeError, "access from outside");
682    }
683    return trace_arg;
684}
685
686struct rb_trace_arg_struct *
687rb_tracearg_from_tracepoint(VALUE tpval)
688{
689    return get_trace_arg();
690}
691
692VALUE
693rb_tracearg_event(rb_trace_arg_t *trace_arg)
694{
695    return ID2SYM(get_event_id(trace_arg->event));
696}
697
698static void
699fill_path_and_lineno(rb_trace_arg_t *trace_arg)
700{
701    if (trace_arg->path == Qundef) {
702	rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(trace_arg->th, trace_arg->cfp);
703
704	if (cfp) {
705	    trace_arg->path = cfp->iseq->location.path;
706	    trace_arg->lineno = rb_vm_get_sourceline(cfp);
707	}
708	else {
709	    trace_arg->path = Qnil;
710	    trace_arg->lineno = 0;
711	}
712    }
713}
714
715VALUE
716rb_tracearg_lineno(rb_trace_arg_t *trace_arg)
717{
718    fill_path_and_lineno(trace_arg);
719    return INT2FIX(trace_arg->lineno);
720}
721VALUE
722rb_tracearg_path(rb_trace_arg_t *trace_arg)
723{
724    fill_path_and_lineno(trace_arg);
725    return trace_arg->path;
726}
727
728static void
729fill_id_and_klass(rb_trace_arg_t *trace_arg)
730{
731    if (!trace_arg->klass_solved) {
732	if (!trace_arg->klass) {
733	    rb_vm_control_frame_id_and_class(trace_arg->cfp, &trace_arg->id, &trace_arg->klass);
734	}
735
736	if (trace_arg->klass) {
737	    if (RB_TYPE_P(trace_arg->klass, T_ICLASS)) {
738		trace_arg->klass = RBASIC(trace_arg->klass)->klass;
739	    }
740	}
741	else {
742	    trace_arg->klass = Qnil;
743	}
744
745	trace_arg->klass_solved = 1;
746    }
747}
748
749VALUE
750rb_tracearg_method_id(rb_trace_arg_t *trace_arg)
751{
752    fill_id_and_klass(trace_arg);
753    return trace_arg->id ? ID2SYM(trace_arg->id) : Qnil;
754}
755
756VALUE
757rb_tracearg_defined_class(rb_trace_arg_t *trace_arg)
758{
759    fill_id_and_klass(trace_arg);
760    return trace_arg->klass;
761}
762
763VALUE
764rb_tracearg_binding(rb_trace_arg_t *trace_arg)
765{
766    rb_control_frame_t *cfp;
767    cfp = rb_vm_get_binding_creatable_next_cfp(trace_arg->th, trace_arg->cfp);
768
769    if (cfp) {
770	return rb_binding_new_with_cfp(trace_arg->th, cfp);
771    }
772    else {
773	return Qnil;
774    }
775}
776
777VALUE
778rb_tracearg_self(rb_trace_arg_t *trace_arg)
779{
780    return trace_arg->self;
781}
782
783VALUE
784rb_tracearg_return_value(rb_trace_arg_t *trace_arg)
785{
786    if (trace_arg->event & (RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN | RUBY_EVENT_B_RETURN)) {
787	/* ok */
788    }
789    else {
790	rb_raise(rb_eRuntimeError, "not supported by this event");
791    }
792    if (trace_arg->data == Qundef) {
793	rb_bug("tp_attr_return_value_m: unreachable");
794    }
795    return trace_arg->data;
796}
797
798VALUE
799rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg)
800{
801    if (trace_arg->event & (RUBY_EVENT_RAISE)) {
802	/* ok */
803    }
804    else {
805	rb_raise(rb_eRuntimeError, "not supported by this event");
806    }
807    if (trace_arg->data == Qundef) {
808	rb_bug("tp_attr_raised_exception_m: unreachable");
809    }
810    return trace_arg->data;
811}
812
813/*
814 * Type of event
815 *
816 * See TracePoint@Events for more information.
817 */
818static VALUE
819tracepoint_attr_event(VALUE tpval)
820{
821    return rb_tracearg_event(get_trace_arg());
822}
823
824/*
825 * Line number of the event
826 */
827static VALUE
828tracepoint_attr_lineno(VALUE tpval)
829{
830    return rb_tracearg_lineno(get_trace_arg());
831}
832
833/*
834 * Path of the file being run
835 */
836static VALUE
837tracepoint_attr_path(VALUE tpval)
838{
839    return rb_tracearg_path(get_trace_arg());
840}
841
842/*
843 * Return the name of the method being called
844 */
845static VALUE
846tracepoint_attr_method_id(VALUE tpval)
847{
848    return rb_tracearg_method_id(get_trace_arg());
849}
850
851/*
852 * Return class or module of the method being called.
853 *
854 *	class C; def foo; end; end
855 * 	trace = TracePoint.new(:call) do |tp|
856 * 	  p tp.defined_class #=> C
857 * 	end.enable do
858 * 	  C.new.foo
859 * 	end
860 *
861 * If method is defined by a module, then that module is returned.
862 *
863 *	module M; def foo; end; end
864 * 	class C; include M; end;
865 * 	trace = TracePoint.new(:call) do |tp|
866 * 	  p tp.defined_class #=> M
867 * 	end.enable do
868 * 	  C.new.foo
869 * 	end
870 *
871 * <b>Note:</b> #defined_class returns singleton class.
872 *
873 * 6th block parameter of Kernel#set_trace_func passes original class
874 * of attached by singleton class.
875 *
876 * <b>This is a difference between Kernel#set_trace_func and TracePoint.</b>
877 *
878 *	class C; def self.foo; end; end
879 * 	trace = TracePoint.new(:call) do |tp|
880 * 	  p tp.defined_class #=> #<Class:C>
881 * 	end.enable do
882 * 	  C.foo
883 * 	end
884 */
885static VALUE
886tracepoint_attr_defined_class(VALUE tpval)
887{
888    return rb_tracearg_defined_class(get_trace_arg());
889}
890
891/*
892 * Return the generated binding object from event
893 */
894static VALUE
895tracepoint_attr_binding(VALUE tpval)
896{
897    return rb_tracearg_binding(get_trace_arg());
898}
899
900/*
901 * Return the trace object during event
902 *
903 * Same as TracePoint#binding:
904 *	trace.binding.eval('self')
905 */
906static VALUE
907tracepoint_attr_self(VALUE tpval)
908{
909    return rb_tracearg_self(get_trace_arg());
910}
911
912/*
913 *  Return value from +:return+, +c_return+, and +b_return+ event
914 */
915static VALUE
916tracepoint_attr_return_value(VALUE tpval)
917{
918    return rb_tracearg_return_value(get_trace_arg());
919}
920
921/*
922 * Value from exception raised on the +:raise+ event
923 */
924static VALUE
925tracepoint_attr_raised_exception(VALUE tpval)
926{
927    return rb_tracearg_raised_exception(get_trace_arg());
928}
929
930static void
931tp_call_trace(VALUE tpval, rb_trace_arg_t *trace_arg)
932{
933    rb_tp_t *tp = tpptr(tpval);
934
935    if (tp->func) {
936	(*tp->func)(tpval, tp->data);
937    }
938    else {
939	rb_proc_call_with_block((VALUE)tp->proc, 1, &tpval, Qnil);
940    }
941}
942
943VALUE
944rb_tracepoint_enable(VALUE tpval)
945{
946    rb_tp_t *tp;
947
948    rb_secure(4);
949    tp = tpptr(tpval);
950
951    if (tp->target_th) {
952	rb_thread_add_event_hook2(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tp->events, tpval,
953				  RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
954    }
955    else {
956	rb_add_event_hook2((rb_event_hook_func_t)tp_call_trace, tp->events, tpval,
957			   RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
958    }
959    tp->tracing = 1;
960    return Qundef;
961}
962
963VALUE
964rb_tracepoint_disable(VALUE tpval)
965{
966    rb_tp_t *tp;
967
968    rb_secure(4);
969    tp = tpptr(tpval);
970
971    if (tp->target_th) {
972	rb_thread_remove_event_hook_with_data(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tpval);
973    }
974    else {
975	rb_remove_event_hook_with_data((rb_event_hook_func_t)tp_call_trace, tpval);
976    }
977    tp->tracing = 0;
978    return Qundef;
979}
980
981/*
982 * call-seq:
983 *	trace.enable		-> true or false
984 *	trace.enable { block }	-> obj
985 *
986 * Activates the trace
987 *
988 * Return true if trace was enabled.
989 * Return false if trace was disabled.
990 *
991 *	trace.enabled?  #=> false
992 *	trace.enable    #=> false (previous state)
993 *                      #   trace is enabled
994 *	trace.enabled?  #=> true
995 *	trace.enable    #=> true (previous state)
996 *                      #   trace is still enabled
997 *
998 * If a block is given, the trace will only be enabled within the scope of the
999 * block.
1000 *
1001 *	trace.enabled?
1002 *	#=> false
1003 *
1004 *	trace.enable do
1005 *	    trace.enabled?
1006 *	    # only enabled for this block
1007 *	end
1008 *
1009 *	trace.enabled?
1010 *	#=> false
1011 *
1012 * Note: You cannot access event hooks within the block.
1013 *
1014 *	trace.enable { p tp.lineno }
1015 *	#=> RuntimeError: access from outside
1016 *
1017 */
1018static VALUE
1019tracepoint_enable_m(VALUE tpval)
1020{
1021    rb_tp_t *tp = tpptr(tpval);
1022    int previous_tracing = tp->tracing;
1023    rb_tracepoint_enable(tpval);
1024
1025    if (rb_block_given_p()) {
1026	return rb_ensure(rb_yield, Qnil,
1027			 previous_tracing ? rb_tracepoint_enable : rb_tracepoint_disable,
1028			 tpval);
1029    }
1030    else {
1031	return previous_tracing ? Qtrue : Qfalse;
1032    }
1033}
1034
1035/*
1036 * call-seq:
1037 *	trace.disable		-> true or false
1038 *	trace.disable { block } -> obj
1039 *
1040 * Deactivates the trace
1041 *
1042 * Return true if trace was enabled.
1043 * Return false if trace was disabled.
1044 *
1045 *	trace.enabled?	#=> true
1046 *	trace.disable	#=> false (previous status)
1047 *	trace.enabled?	#=> false
1048 *	trace.disable	#=> false
1049 *
1050 * If a block is given, the trace will only be disable within the scope of the
1051 * block.
1052 *
1053 *	trace.enabled?
1054 *	#=> true
1055 *
1056 *	trace.disable do
1057 *	    trace.enabled?
1058 *	    # only disabled for this block
1059 *	end
1060 *
1061 *	trace.enabled?
1062 *	#=> true
1063 *
1064 * Note: You cannot access event hooks within the block.
1065 *
1066 *	trace.disable { p tp.lineno }
1067 *	#=> RuntimeError: access from outside
1068 */
1069static VALUE
1070tracepoint_disable_m(VALUE tpval)
1071{
1072    rb_tp_t *tp = tpptr(tpval);
1073    int previous_tracing = tp->tracing;
1074    rb_tracepoint_disable(tpval);
1075
1076    if (rb_block_given_p()) {
1077	return rb_ensure(rb_yield, Qnil,
1078			 previous_tracing ? rb_tracepoint_enable : rb_tracepoint_disable,
1079			 tpval);
1080    }
1081    else {
1082	return previous_tracing ? Qtrue : Qfalse;
1083    }
1084}
1085
1086/*
1087 * call-seq:
1088 *	trace.enabled?	    -> true or false
1089 *
1090 * The current status of the trace
1091 */
1092VALUE
1093rb_tracepoint_enabled_p(VALUE tpval)
1094{
1095    rb_tp_t *tp = tpptr(tpval);
1096    return tp->tracing ? Qtrue : Qfalse;
1097}
1098
1099static VALUE
1100tracepoint_new(VALUE klass, rb_thread_t *target_th, rb_event_flag_t events, void (func)(VALUE, void*), void *data, VALUE proc)
1101{
1102    VALUE tpval = tp_alloc(klass);
1103    rb_tp_t *tp;
1104    TypedData_Get_Struct(tpval, rb_tp_t, &tp_data_type, tp);
1105
1106    tp->proc = proc;
1107    tp->func = func;
1108    tp->data = data;
1109    tp->events = events;
1110    tp->self = tpval;
1111
1112    return tpval;
1113}
1114
1115VALUE
1116rb_tracepoint_new(VALUE target_thval, rb_event_flag_t events, void (*func)(VALUE, void *), void *data)
1117{
1118    rb_thread_t *target_th = 0;
1119    if (RTEST(target_thval)) {
1120	GetThreadPtr(target_thval, target_th);
1121	/* TODO: Test it!
1122	 * Warning: This function is not tested.
1123	 */
1124    }
1125    return tracepoint_new(rb_cTracePoint, target_th, events, func, data, Qundef);
1126}
1127
1128/*
1129 * call-seq:
1130 *	TracePoint.new(*events) { |obj| block }	    -> obj
1131 *
1132 * Returns a new TracePoint object, not enabled by default.
1133 *
1134 * Next, in order to activate the trace, you must use TracePoint.enable
1135 *
1136 *	trace = TracePoint.new(:call) do |tp|
1137 *	    p [tp.lineno, tp.defined_class, tp.method_id, tp.event]
1138 *	end
1139 *	#=> #<TracePoint:0x007f17372cdb20>
1140 *
1141 *	trace.enable
1142 *	#=> #<TracePoint:0x007f17372cdb20>
1143 *
1144 *	puts "Hello, TracePoint!"
1145 *	# ...
1146 *	# [48, IRB::Notifier::AbstractNotifier, :printf, :call]
1147 *	# ...
1148 *
1149 * When you want to deactivate the trace, you must use TracePoint.disable
1150 *
1151 *	trace.disable
1152 *
1153 * See TracePoint@Events for possible events and more information.
1154 *
1155 * A block must be given, otherwise a ThreadError is raised.
1156 *
1157 * If the trace method isn't included in the given events filter, a
1158 * RuntimeError is raised.
1159 *
1160 *	TracePoint.trace(:line) do |tp|
1161 *	    p tp.raised_exception
1162 *	end
1163 *	#=> RuntimeError: 'raised_exception' not supported by this event
1164 *
1165 * If the trace method is called outside block, a RuntimeError is raised.
1166 *
1167 *      TracePoint.trace(:line) do |tp|
1168 *        $tp = tp
1169 *      end
1170 *      $tp.line #=> access from outside (RuntimeError)
1171 *
1172 * Access from other threads is also forbidden.
1173 *
1174 */
1175static VALUE
1176tracepoint_new_s(int argc, VALUE *argv, VALUE self)
1177{
1178    rb_event_flag_t events = 0;
1179    int i;
1180
1181    if (argc > 0) {
1182	for (i=0; i<argc; i++) {
1183	    events |= symbol2event_flag(argv[i]);
1184	}
1185    }
1186    else {
1187	events = RUBY_EVENT_TRACEPOINT_ALL;
1188    }
1189
1190    if (!rb_block_given_p()) {
1191	rb_raise(rb_eThreadError, "must be called with a block");
1192    }
1193
1194    return tracepoint_new(self, 0, events, 0, 0, rb_block_proc());
1195}
1196
1197static VALUE
1198tracepoint_trace_s(int argc, VALUE *argv, VALUE self)
1199{
1200    VALUE trace = tracepoint_new_s(argc, argv, self);
1201    rb_tracepoint_enable(trace);
1202    return trace;
1203}
1204
1205/*
1206 *  call-seq:
1207 *    trace.inspect  -> string
1208 *
1209 *  Return a string containing a human-readable TracePoint
1210 *  status.
1211 */
1212
1213static VALUE
1214tracepoint_inspect(VALUE self)
1215{
1216    rb_tp_t *tp = tpptr(self);
1217    rb_trace_arg_t *trace_arg = GET_THREAD()->trace_arg;
1218
1219    if (trace_arg) {
1220	switch (trace_arg->event) {
1221	  case RUBY_EVENT_LINE:
1222	  case RUBY_EVENT_SPECIFIED_LINE:
1223	    {
1224		VALUE sym = rb_tracearg_method_id(trace_arg);
1225		if (NIL_P(sym))
1226		  goto default_inspect;
1227		return rb_sprintf("#<TracePoint:%"PRIsVALUE"@%"PRIsVALUE":%d in `%"PRIsVALUE"'>",
1228				  rb_tracearg_event(trace_arg),
1229				  rb_tracearg_path(trace_arg),
1230				  FIX2INT(rb_tracearg_lineno(trace_arg)),
1231				  sym);
1232	    }
1233	  case RUBY_EVENT_CALL:
1234	  case RUBY_EVENT_C_CALL:
1235	  case RUBY_EVENT_RETURN:
1236	  case RUBY_EVENT_C_RETURN:
1237	    return rb_sprintf("#<TracePoint:%"PRIsVALUE" `%"PRIsVALUE"'@%"PRIsVALUE":%d>",
1238			      rb_tracearg_event(trace_arg),
1239			      rb_tracearg_method_id(trace_arg),
1240			      rb_tracearg_path(trace_arg),
1241			      FIX2INT(rb_tracearg_lineno(trace_arg)));
1242	  case RUBY_EVENT_THREAD_BEGIN:
1243	  case RUBY_EVENT_THREAD_END:
1244	    return rb_sprintf("#<TracePoint:%"PRIsVALUE" %"PRIsVALUE">",
1245			      rb_tracearg_event(trace_arg),
1246			      rb_tracearg_self(trace_arg));
1247	  default:
1248	  default_inspect:
1249	    return rb_sprintf("#<TracePoint:%"PRIsVALUE"@%"PRIsVALUE":%d>",
1250			      rb_tracearg_event(trace_arg),
1251			      rb_tracearg_path(trace_arg),
1252			      FIX2INT(rb_tracearg_lineno(trace_arg)));
1253	}
1254    }
1255    else {
1256	return rb_sprintf("#<TracePoint:%s>", tp->tracing ? "enabled" : "disabled");
1257    }
1258}
1259
1260/* This function is called from inits.c */
1261void
1262Init_vm_trace(void)
1263{
1264    /* trace_func */
1265    rb_define_global_function("set_trace_func", set_trace_func, 1);
1266    rb_define_method(rb_cThread, "set_trace_func", thread_set_trace_func_m, 1);
1267    rb_define_method(rb_cThread, "add_trace_func", thread_add_trace_func_m, 1);
1268
1269    /*
1270     * Document-class: TracePoint
1271     *
1272     * A class that provides the functionality of Kernel#set_trace_func in a
1273     * nice Object-Oriented API.
1274     *
1275     * == Example
1276     *
1277     * We can use TracePoint to gather information specifically for exceptions:
1278     *
1279     *	    trace = TracePoint.new(:raise) do |tp|
1280     *		p [tp.lineno, tp.event, tp.raised_exception]
1281     *	    end
1282     *	    #=> #<TracePoint:0x007f786a452448>
1283     *
1284     *	    trace.enable
1285     *	    #=> #<TracePoint:0x007f786a452448>
1286     *
1287     *	    0 / 0
1288     *	    #=> [5, :raise, #<ZeroDivisionError: divided by 0>]
1289     *
1290     * == Events
1291     *
1292     * If you don't specify the type of events you want to listen for,
1293     * TracePoint will include all available events.
1294     *
1295     * *Note* do not depend on current event set, as this list is subject to
1296     * change. Instead, it is recommended you specify the type of events you
1297     * want to use.
1298     *
1299     * To filter what is traced, you can pass any of the following as +events+:
1300     *
1301     * +:line+:: execute code on a new line
1302     * +:class+:: start a class or module definition
1303     * +:end+:: finish a class or module definition
1304     * +:call+:: call a Ruby method
1305     * +:return+:: return from a Ruby method
1306     * +:c_call+:: call a C-language routine
1307     * +:c_return+:: return from a C-language routine
1308     * +:raise+:: raise an exception
1309     * +:b_call+:: event hook at block entry
1310     * +:b_return+:: event hook at block ending
1311     * +:thread_begin+:: event hook at thread beginning
1312     * +:thread_end+:: event hook at thread ending
1313     *
1314     */
1315    rb_cTracePoint = rb_define_class("TracePoint", rb_cObject);
1316    rb_undef_alloc_func(rb_cTracePoint);
1317    rb_undef_method(CLASS_OF(rb_cTracePoint), "new");
1318    rb_define_singleton_method(rb_cTracePoint, "new", tracepoint_new_s, -1);
1319    /*
1320     * Document-method: trace
1321     *
1322     * call-seq:
1323     *	TracePoint.trace(*events) { |obj| block }	-> obj
1324     *
1325     *  A convenience method for TracePoint.new, that activates the trace
1326     *  automatically.
1327     *
1328     *	    trace = TracePoint.trace(:call) { |tp| [tp.lineno, tp.event] }
1329     *	    #=> #<TracePoint:0x007f786a452448>
1330     *
1331     *	    trace.enabled? #=> true
1332     */
1333    rb_define_singleton_method(rb_cTracePoint, "trace", tracepoint_trace_s, -1);
1334
1335    rb_define_method(rb_cTracePoint, "enable", tracepoint_enable_m, 0);
1336    rb_define_method(rb_cTracePoint, "disable", tracepoint_disable_m, 0);
1337    rb_define_method(rb_cTracePoint, "enabled?", rb_tracepoint_enabled_p, 0);
1338
1339    rb_define_method(rb_cTracePoint, "inspect", tracepoint_inspect, 0);
1340
1341    rb_define_method(rb_cTracePoint, "event", tracepoint_attr_event, 0);
1342    rb_define_method(rb_cTracePoint, "lineno", tracepoint_attr_lineno, 0);
1343    rb_define_method(rb_cTracePoint, "path", tracepoint_attr_path, 0);
1344    rb_define_method(rb_cTracePoint, "method_id", tracepoint_attr_method_id, 0);
1345    rb_define_method(rb_cTracePoint, "defined_class", tracepoint_attr_defined_class, 0);
1346    rb_define_method(rb_cTracePoint, "binding", tracepoint_attr_binding, 0);
1347    rb_define_method(rb_cTracePoint, "self", tracepoint_attr_self, 0);
1348    rb_define_method(rb_cTracePoint, "return_value", tracepoint_attr_return_value, 0);
1349    rb_define_method(rb_cTracePoint, "raised_exception", tracepoint_attr_raised_exception, 0);
1350}
1351
1352