2012-08-15 08:39:10 +04:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
vm_trace.c -
|
|
|
|
|
|
|
|
$Author: ko1 $
|
|
|
|
created at: Tue Aug 14 19:37:09 2012
|
|
|
|
|
|
|
|
Copyright (C) 1993-2012 Yukihiro Matsumoto
|
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
/*
|
2014-01-19 06:14:45 +04:00
|
|
|
* This file include two parts:
|
2012-08-15 08:39:10 +04:00
|
|
|
*
|
|
|
|
* (1) set_trace_func internal mechanisms
|
|
|
|
* and C level API
|
|
|
|
*
|
|
|
|
* (2) Ruby level API
|
|
|
|
* (2-1) set_trace_func API
|
|
|
|
* (2-2) TracePoint API (not yet)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2014-11-15 14:49:06 +03:00
|
|
|
#include "internal.h"
|
2012-11-20 16:57:49 +04:00
|
|
|
#include "ruby/debug.h"
|
2012-08-15 08:39:10 +04:00
|
|
|
|
|
|
|
#include "vm_core.h"
|
2018-06-23 17:11:19 +03:00
|
|
|
#include "mjit.h"
|
2017-11-14 15:58:36 +03:00
|
|
|
#include "iseq.h"
|
2012-08-15 08:39:10 +04:00
|
|
|
#include "eval_intern.h"
|
|
|
|
|
|
|
|
/* (1) trace mechanisms */
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
typedef struct rb_event_hook_struct {
|
2012-11-20 16:57:49 +04:00
|
|
|
rb_event_hook_flag_t hook_flags;
|
2012-08-16 15:41:24 +04:00
|
|
|
rb_event_flag_t events;
|
|
|
|
rb_event_hook_func_t func;
|
|
|
|
VALUE data;
|
|
|
|
struct rb_event_hook_struct *next;
|
2017-11-15 16:21:24 +03:00
|
|
|
|
|
|
|
struct {
|
|
|
|
rb_thread_t *th;
|
2018-11-26 23:16:14 +03:00
|
|
|
unsigned int target_line;
|
2017-11-15 16:21:24 +03:00
|
|
|
} filter;
|
2012-08-16 15:41:24 +04:00
|
|
|
} rb_event_hook_t;
|
2012-08-15 08:39:10 +04:00
|
|
|
|
2012-08-22 09:12:31 +04:00
|
|
|
typedef void (*rb_event_hook_raw_arg_func_t)(VALUE data, const rb_trace_arg_t *arg);
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
#define MAX_EVENT_NUM 32
|
2012-08-15 08:39:10 +04:00
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
void
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_hook_list_mark(rb_hook_list_t *hooks)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2012-08-16 15:41:24 +04:00
|
|
|
rb_event_hook_t *hook = hooks->hooks;
|
|
|
|
|
|
|
|
while (hook) {
|
|
|
|
rb_gc_mark(hook->data);
|
|
|
|
hook = hook->next;
|
|
|
|
}
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
static void clean_hooks(const rb_execution_context_t *ec, rb_hook_list_t *list);
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_hook_list_free(rb_hook_list_t *hooks)
|
|
|
|
{
|
|
|
|
clean_hooks(GET_EC(), hooks);
|
|
|
|
}
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
/* ruby_vm_event_flags management */
|
|
|
|
|
2017-11-14 15:58:36 +03:00
|
|
|
static void
|
|
|
|
update_global_event_hook(rb_event_flag_t vm_events)
|
|
|
|
{
|
2017-11-18 12:39:41 +03:00
|
|
|
rb_event_flag_t new_iseq_events = vm_events & ISEQ_TRACE_EVENTS;
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_event_flag_t enabled_iseq_events = ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS;
|
2017-11-17 09:24:55 +03:00
|
|
|
|
2017-12-11 22:17:25 +03:00
|
|
|
if (new_iseq_events & ~enabled_iseq_events) {
|
2018-06-23 17:11:19 +03:00
|
|
|
/* Stop calling all JIT-ed code. Compiling trace insns is not supported for now. */
|
2018-10-20 09:53:00 +03:00
|
|
|
#if USE_MJIT
|
2018-06-23 17:11:19 +03:00
|
|
|
mjit_call_p = FALSE;
|
2018-10-20 09:53:00 +03:00
|
|
|
#endif
|
2018-06-23 17:11:19 +03:00
|
|
|
|
2017-11-17 09:24:55 +03:00
|
|
|
/* write all ISeqs iff new events are added */
|
2017-12-11 22:17:25 +03:00
|
|
|
rb_iseq_trace_set_all(new_iseq_events | enabled_iseq_events);
|
2017-11-14 16:25:11 +03:00
|
|
|
}
|
2017-11-17 09:24:55 +03:00
|
|
|
|
2017-11-14 15:58:36 +03:00
|
|
|
ruby_vm_event_flags = vm_events;
|
2018-11-26 21:16:39 +03:00
|
|
|
ruby_vm_event_enabled_global_flags |= vm_events;
|
2017-11-14 15:58:36 +03:00
|
|
|
rb_objspace_set_event_hook(vm_events);
|
|
|
|
}
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
/* add/remove hooks */
|
|
|
|
|
|
|
|
static rb_event_hook_t *
|
2012-11-20 16:57:49 +04:00
|
|
|
alloc_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2013-11-26 12:41:44 +04:00
|
|
|
rb_event_hook_t *hook;
|
|
|
|
|
|
|
|
if ((events & RUBY_INTERNAL_EVENT_MASK) && (events & ~RUBY_INTERNAL_EVENT_MASK)) {
|
|
|
|
rb_raise(rb_eTypeError, "Can not specify normal event and internal event simultaneously.");
|
|
|
|
}
|
|
|
|
|
|
|
|
hook = ALLOC(rb_event_hook_t);
|
2012-08-16 15:41:24 +04:00
|
|
|
hook->hook_flags = hook_flags;
|
|
|
|
hook->events = events;
|
|
|
|
hook->func = func;
|
|
|
|
hook->data = data;
|
2017-11-15 16:21:24 +03:00
|
|
|
|
|
|
|
/* no filters */
|
2018-11-26 23:16:14 +03:00
|
|
|
hook->filter.th = NULL;
|
|
|
|
hook->filter.target_line = 0;
|
2017-11-15 16:21:24 +03:00
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
return hook;
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
static void
|
2018-11-26 21:16:39 +03:00
|
|
|
hook_list_connect(VALUE list_owner, rb_hook_list_t *list, rb_event_hook_t *hook, int global_p)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2012-08-16 15:41:24 +04:00
|
|
|
hook->next = list->hooks;
|
|
|
|
list->hooks = hook;
|
|
|
|
list->events |= hook->events;
|
2018-11-26 21:16:39 +03:00
|
|
|
|
|
|
|
if (global_p) {
|
|
|
|
/* global hooks are root objects at GC mark. */
|
|
|
|
update_global_event_hook(list->events);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
RB_OBJ_WRITTEN(list_owner, Qundef, hook->data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
connect_event_hook(const rb_execution_context_t *ec, rb_event_hook_t *hook)
|
|
|
|
{
|
|
|
|
rb_hook_list_t *list = rb_vm_global_hooks(ec);
|
|
|
|
hook_list_connect(Qundef, list, hook, TRUE);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-15 16:21:24 +03:00
|
|
|
rb_threadptr_add_event_hook(const rb_execution_context_t *ec, rb_thread_t *th,
|
|
|
|
rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2012-08-16 15:41:24 +04:00
|
|
|
rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags);
|
2017-11-15 16:21:24 +03:00
|
|
|
hook->filter.th = th;
|
|
|
|
connect_event_hook(ec, hook);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
void
|
|
|
|
rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2017-11-15 16:21:24 +03:00
|
|
|
rb_threadptr_add_event_hook(GET_EC(), rb_thread_ptr(thval), func, events, data, RUBY_EVENT_HOOK_FLAG_SAFE);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
void
|
|
|
|
rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2012-11-20 16:57:49 +04:00
|
|
|
rb_event_hook_t *hook = alloc_event_hook(func, events, data, RUBY_EVENT_HOOK_FLAG_SAFE);
|
2017-11-15 16:21:24 +03:00
|
|
|
connect_event_hook(GET_EC(), hook);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-11-20 16:57:49 +04:00
|
|
|
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)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2017-11-15 16:21:24 +03:00
|
|
|
rb_threadptr_add_event_hook(GET_EC(), rb_thread_ptr(thval), func, events, data, hook_flags);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-11-20 16:57:49 +04:00
|
|
|
rb_add_event_hook2(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flags)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2012-08-22 09:12:31 +04:00
|
|
|
rb_event_hook_t *hook = alloc_event_hook(func, events, data, hook_flags);
|
2017-11-15 16:21:24 +03:00
|
|
|
connect_event_hook(GET_EC(), hook);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
2017-11-16 05:47:58 +03:00
|
|
|
static void
|
2018-11-26 21:16:39 +03:00
|
|
|
clean_hooks(const rb_execution_context_t *ec, rb_hook_list_t *list)
|
2017-11-16 05:47:58 +03:00
|
|
|
{
|
|
|
|
rb_event_hook_t *hook, **nextp = &list->hooks;
|
|
|
|
VM_ASSERT(list->need_clean == TRUE);
|
|
|
|
|
|
|
|
list->events = 0;
|
|
|
|
list->need_clean = FALSE;
|
|
|
|
|
|
|
|
while ((hook = *nextp) != 0) {
|
|
|
|
if (hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) {
|
|
|
|
*nextp = hook->next;
|
|
|
|
xfree(hook);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
list->events |= hook->events; /* update active events */
|
|
|
|
nextp = &hook->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
if (list == rb_vm_global_hooks(ec)) {
|
|
|
|
/* global events */
|
|
|
|
update_global_event_hook(list->events);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* local events */
|
|
|
|
if (list->events == 0) {
|
|
|
|
ruby_xfree(list);
|
|
|
|
}
|
|
|
|
}
|
2017-11-16 05:47:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-11-26 21:16:39 +03:00
|
|
|
clean_hooks_check(const rb_execution_context_t *ec, rb_hook_list_t *list)
|
2017-11-16 05:47:58 +03:00
|
|
|
{
|
|
|
|
if (UNLIKELY(list->need_clean != FALSE)) {
|
2018-11-26 21:16:54 +03:00
|
|
|
if (list->running == 0) {
|
2018-11-26 21:16:39 +03:00
|
|
|
clean_hooks(ec, list);
|
|
|
|
}
|
2017-11-16 05:47:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-15 16:21:24 +03:00
|
|
|
#define MATCH_ANY_FILTER_TH ((rb_thread_t *)1)
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
/* if func is 0, then clear all funcs */
|
2012-08-15 08:39:10 +04:00
|
|
|
static int
|
2017-11-15 16:21:24 +03:00
|
|
|
remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th, rb_event_hook_func_t func, VALUE data)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2017-11-16 05:47:58 +03:00
|
|
|
rb_vm_t *vm = rb_ec_vm_ptr(ec);
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_hook_list_t *list = &vm->global_hooks;
|
2012-08-16 15:41:24 +04:00
|
|
|
int ret = 0;
|
|
|
|
rb_event_hook_t *hook = list->hooks;
|
|
|
|
|
2012-08-15 08:39:10 +04:00
|
|
|
while (hook) {
|
|
|
|
if (func == 0 || hook->func == func) {
|
2017-11-15 16:21:24 +03:00
|
|
|
if (hook->filter.th == filter_th || filter_th == MATCH_ANY_FILTER_TH) {
|
|
|
|
if (data == Qundef || hook->data == data) {
|
|
|
|
hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED;
|
|
|
|
ret+=1;
|
|
|
|
list->need_clean = TRUE;
|
|
|
|
}
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
hook = hook->next;
|
|
|
|
}
|
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
clean_hooks_check(ec, list);
|
2012-08-16 15:41:24 +04:00
|
|
|
return ret;
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-11-15 16:21:24 +03:00
|
|
|
rb_threadptr_remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th, rb_event_hook_func_t func, VALUE data)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2017-11-15 16:21:24 +03:00
|
|
|
return remove_event_hook(ec, filter_th, func, data);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func)
|
|
|
|
{
|
2017-11-15 16:21:24 +03:00
|
|
|
return rb_threadptr_remove_event_hook(GET_EC(), rb_thread_ptr(thval), func, Qundef);
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_thread_remove_event_hook_with_data(VALUE thval, rb_event_hook_func_t func, VALUE data)
|
|
|
|
{
|
2017-11-15 16:21:24 +03:00
|
|
|
return rb_threadptr_remove_event_hook(GET_EC(), rb_thread_ptr(thval), func, data);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
int
|
|
|
|
rb_remove_event_hook(rb_event_hook_func_t func)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2017-11-15 16:21:24 +03:00
|
|
|
return remove_event_hook(GET_EC(), NULL, func, Qundef);
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_remove_event_hook_with_data(rb_event_hook_func_t func, VALUE data)
|
|
|
|
{
|
2017-11-15 16:21:24 +03:00
|
|
|
return remove_event_hook(GET_EC(), NULL, func, data);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
void
|
|
|
|
rb_clear_trace_func(void)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2017-11-15 16:21:24 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
rb_threadptr_remove_event_hook(ec, MATCH_ANY_FILTER_TH, 0, Qundef);
|
|
|
|
}
|
2014-05-28 05:48:11 +04:00
|
|
|
|
2017-11-15 16:21:24 +03:00
|
|
|
void
|
|
|
|
rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec)
|
|
|
|
{
|
|
|
|
rb_threadptr_remove_event_hook(ec, rb_ec_thread_ptr(ec), 0, Qundef);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
/* invoke hooks */
|
|
|
|
|
2013-11-26 13:42:04 +04:00
|
|
|
static void
|
2017-11-07 07:01:13 +03:00
|
|
|
exec_hooks_body(const rb_execution_context_t *ec, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
|
2013-11-26 13:42:04 +04:00
|
|
|
{
|
|
|
|
rb_event_hook_t *hook;
|
|
|
|
|
|
|
|
for (hook = list->hooks; hook; hook = hook->next) {
|
2017-11-15 16:21:24 +03:00
|
|
|
if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) &&
|
|
|
|
(trace_arg->event & hook->events) &&
|
2018-11-26 23:16:14 +03:00
|
|
|
(LIKELY(hook->filter.th == 0) || hook->filter.th == rb_ec_thread_ptr(ec)) &&
|
|
|
|
(LIKELY(hook->filter.target_line == 0) || (hook->filter.target_line == (unsigned int)rb_vm_get_sourceline(ec->cfp)))) {
|
2013-11-26 13:42:04 +04:00
|
|
|
if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_RAW_ARG)) {
|
|
|
|
(*hook->func)(trace_arg->event, hook->data, trace_arg->self, trace_arg->id, trace_arg->klass);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
(*((rb_event_hook_raw_arg_func_t)hook->func))(hook->data, trace_arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-22 09:12:31 +04:00
|
|
|
static int
|
2018-11-26 21:16:39 +03:00
|
|
|
exec_hooks_precheck(const rb_execution_context_t *ec, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
|
2013-11-26 13:42:04 +04:00
|
|
|
{
|
2017-11-16 05:47:58 +03:00
|
|
|
if (list->events & trace_arg->event) {
|
2018-11-26 21:16:39 +03:00
|
|
|
list->running++;
|
2017-11-16 05:47:58 +03:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return FALSE;
|
2013-11-26 13:42:04 +04:00
|
|
|
}
|
2017-11-16 05:47:58 +03:00
|
|
|
}
|
2015-11-08 02:10:55 +03:00
|
|
|
|
2017-11-16 05:47:58 +03:00
|
|
|
static void
|
2018-11-26 21:16:39 +03:00
|
|
|
exec_hooks_postcheck(const rb_execution_context_t *ec, rb_hook_list_t *list)
|
2017-11-16 05:47:58 +03:00
|
|
|
{
|
2018-11-26 21:16:39 +03:00
|
|
|
list->running--;
|
|
|
|
clean_hooks_check(ec, list);
|
2013-11-26 13:42:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-11-26 21:16:39 +03:00
|
|
|
exec_hooks_unprotected(const rb_execution_context_t *ec, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
|
2013-11-26 13:42:04 +04:00
|
|
|
{
|
2018-11-26 21:16:39 +03:00
|
|
|
if (exec_hooks_precheck(ec, list, trace_arg) == 0) return;
|
2017-11-07 07:01:13 +03:00
|
|
|
exec_hooks_body(ec, list, trace_arg);
|
2018-11-26 21:16:39 +03:00
|
|
|
exec_hooks_postcheck(ec, list);
|
2013-11-26 13:42:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2018-11-26 21:16:39 +03:00
|
|
|
exec_hooks_protected(rb_execution_context_t *ec, rb_hook_list_t *list, const rb_trace_arg_t *trace_arg)
|
2012-08-16 15:41:24 +04:00
|
|
|
{
|
2017-06-23 10:25:52 +03:00
|
|
|
enum ruby_tag_type state;
|
2012-08-16 15:41:24 +04:00
|
|
|
volatile int raised;
|
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
if (exec_hooks_precheck(ec, list, trace_arg) == 0) return 0;
|
2012-08-15 08:39:10 +04:00
|
|
|
|
2017-11-07 07:01:13 +03:00
|
|
|
raised = rb_ec_reset_raised(ec);
|
2012-08-16 15:41:24 +04:00
|
|
|
|
2012-11-20 16:57:49 +04:00
|
|
|
/* TODO: Support !RUBY_EVENT_HOOK_FLAG_SAFE hooks */
|
2012-08-16 15:41:24 +04:00
|
|
|
|
2017-11-07 07:01:13 +03:00
|
|
|
EC_PUSH_TAG(ec);
|
2017-10-26 14:02:13 +03:00
|
|
|
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
|
2017-11-07 07:01:13 +03:00
|
|
|
exec_hooks_body(ec, list, trace_arg);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_POP_TAG();
|
2012-08-15 08:39:10 +04:00
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
exec_hooks_postcheck(ec, list);
|
2017-11-16 05:47:58 +03:00
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
if (raised) {
|
2017-11-07 08:12:39 +03:00
|
|
|
rb_ec_set_raised(ec);
|
2012-08-16 15:41:24 +04:00
|
|
|
}
|
2012-08-15 08:39:10 +04:00
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
return state;
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
mjit_compile.c: merge initial JIT compiler
which has been developed by Takashi Kokubun <takashikkbn@gmail> as
YARV-MJIT. Many of its bugs are fixed by wanabe <s.wanabe@gmail.com>.
This JIT compiler is designed to be a safe migration path to introduce
JIT compiler to MRI. So this commit does not include any bytecode
changes or dynamic instruction modifications, which are done in original
MJIT.
This commit even strips off some aggressive optimizations from
YARV-MJIT, and thus it's slower than YARV-MJIT too. But it's still
fairly faster than Ruby 2.5 in some benchmarks (attached below).
Note that this JIT compiler passes `make test`, `make test-all`, `make
test-spec` without JIT, and even with JIT. Not only it's perfectly safe
with JIT disabled because it does not replace VM instructions unlike
MJIT, but also with JIT enabled it stably runs Ruby applications
including Rails applications.
I'm expecting this version as just "initial" JIT compiler. I have many
optimization ideas which are skipped for initial merging, and you may
easily replace this JIT compiler with a faster one by just replacing
mjit_compile.c. `mjit_compile` interface is designed for the purpose.
common.mk: update dependencies for mjit_compile.c.
internal.h: declare `rb_vm_insn_addr2insn` for MJIT.
vm.c: exclude some definitions if `-DMJIT_HEADER` is provided to
compiler. This avoids to include some functions which take a long time
to compile, e.g. vm_exec_core. Some of the purpose is achieved in
transform_mjit_header.rb (see `IGNORED_FUNCTIONS`) but others are
manually resolved for now. Load mjit_helper.h for MJIT header.
mjit_helper.h: New. This is a file used only by JIT-ed code. I'll
refactor `mjit_call_cfunc` later.
vm_eval.c: add some #ifdef switches to skip compiling some functions
like Init_vm_eval.
win32/mkexports.rb: export thread/ec functions, which are used by MJIT.
include/ruby/defines.h: add MJIT_FUNC_EXPORTED macro alis to clarify
that a function is exported only for MJIT.
array.c: export a function used by MJIT.
bignum.c: ditto.
class.c: ditto.
compile.c: ditto.
error.c: ditto.
gc.c: ditto.
hash.c: ditto.
iseq.c: ditto.
numeric.c: ditto.
object.c: ditto.
proc.c: ditto.
re.c: ditto.
st.c: ditto.
string.c: ditto.
thread.c: ditto.
variable.c: ditto.
vm_backtrace.c: ditto.
vm_insnhelper.c: ditto.
vm_method.c: ditto.
I would like to improve maintainability of function exports, but I
believe this way is acceptable as initial merging if we clarify the
new exports are for MJIT (so that we can use them as TODO list to fix)
and add unit tests to detect unresolved symbols.
I'll add unit tests of JIT compilations in succeeding commits.
Author: Takashi Kokubun <takashikkbn@gmail.com>
Contributor: wanabe <s.wanabe@gmail.com>
Part of [Feature #14235]
---
* Known issues
* Code generated by gcc is faster than clang. The benchmark may be worse
in macOS. Following benchmark result is provided by gcc w/ Linux.
* Performance is decreased when Google Chrome is running
* JIT can work on MinGW, but it doesn't improve performance at least
in short running benchmark.
* Currently it doesn't perform well with Rails. We'll try to fix this
before release.
---
* Benchmark reslts
Benchmarked with:
Intel 4.0GHz i7-4790K with 16GB memory under x86-64 Ubuntu 8 Cores
- 2.0.0-p0: Ruby 2.0.0-p0
- r62186: Ruby trunk (early 2.6.0), before MJIT changes
- JIT off: On this commit, but without `--jit` option
- JIT on: On this commit, and with `--jit` option
** Optcarrot fps
Benchmark: https://github.com/mame/optcarrot
| |2.0.0-p0 |r62186 |JIT off |JIT on |
|:--------|:--------|:--------|:--------|:--------|
|fps |37.32 |51.46 |51.31 |58.88 |
|vs 2.0.0 |1.00x |1.38x |1.37x |1.58x |
** MJIT benchmarks
Benchmark: https://github.com/benchmark-driver/mjit-benchmarks
(Original: https://github.com/vnmakarov/ruby/tree/rtl_mjit_branch/MJIT-benchmarks)
| |2.0.0-p0 |r62186 |JIT off |JIT on |
|:----------|:--------|:--------|:--------|:--------|
|aread |1.00 |1.09 |1.07 |2.19 |
|aref |1.00 |1.13 |1.11 |2.22 |
|aset |1.00 |1.50 |1.45 |2.64 |
|awrite |1.00 |1.17 |1.13 |2.20 |
|call |1.00 |1.29 |1.26 |2.02 |
|const2 |1.00 |1.10 |1.10 |2.19 |
|const |1.00 |1.11 |1.10 |2.19 |
|fannk |1.00 |1.04 |1.02 |1.00 |
|fib |1.00 |1.32 |1.31 |1.84 |
|ivread |1.00 |1.13 |1.12 |2.43 |
|ivwrite |1.00 |1.23 |1.21 |2.40 |
|mandelbrot |1.00 |1.13 |1.16 |1.28 |
|meteor |1.00 |2.97 |2.92 |3.17 |
|nbody |1.00 |1.17 |1.15 |1.49 |
|nest-ntimes|1.00 |1.22 |1.20 |1.39 |
|nest-while |1.00 |1.10 |1.10 |1.37 |
|norm |1.00 |1.18 |1.16 |1.24 |
|nsvb |1.00 |1.16 |1.16 |1.17 |
|red-black |1.00 |1.02 |0.99 |1.12 |
|sieve |1.00 |1.30 |1.28 |1.62 |
|trees |1.00 |1.14 |1.13 |1.19 |
|while |1.00 |1.12 |1.11 |2.41 |
** Discourse's script/bench.rb
Benchmark: https://github.com/discourse/discourse/blob/v1.8.7/script/bench.rb
NOTE: Rails performance was somehow a little degraded with JIT for now.
We should fix this.
(At least I know opt_aref is performing badly in JIT and I have an idea
to fix it. Please wait for the fix.)
*** JIT off
Your Results: (note for timings- percentile is first, duration is second in millisecs)
categories_admin:
50: 17
75: 18
90: 22
99: 29
home_admin:
50: 21
75: 21
90: 27
99: 40
topic_admin:
50: 17
75: 18
90: 22
99: 32
categories:
50: 35
75: 41
90: 43
99: 77
home:
50: 39
75: 46
90: 49
99: 95
topic:
50: 46
75: 52
90: 56
99: 101
*** JIT on
Your Results: (note for timings- percentile is first, duration is second in millisecs)
categories_admin:
50: 19
75: 21
90: 25
99: 33
home_admin:
50: 24
75: 26
90: 30
99: 35
topic_admin:
50: 19
75: 20
90: 25
99: 30
categories:
50: 40
75: 44
90: 48
99: 76
home:
50: 42
75: 48
90: 51
99: 89
topic:
50: 49
75: 55
90: 58
99: 99
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62197 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 14:22:28 +03:00
|
|
|
MJIT_FUNC_EXPORTED void
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_exec_event_hooks(rb_trace_arg_t *trace_arg, rb_hook_list_t *hooks, int pop_p)
|
2012-08-16 15:41:24 +04:00
|
|
|
{
|
2017-10-29 16:17:37 +03:00
|
|
|
rb_execution_context_t *ec = trace_arg->ec;
|
2013-11-26 13:42:04 +04:00
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
if (UNLIKELY(trace_arg->event & RUBY_INTERNAL_EVENT_MASK)) {
|
|
|
|
if (ec->trace_arg && (ec->trace_arg->event & RUBY_INTERNAL_EVENT_MASK)) {
|
|
|
|
/* skip hooks because this thread doing INTERNAL_EVENT */
|
2013-11-26 13:42:04 +04:00
|
|
|
}
|
|
|
|
else {
|
2017-10-29 16:17:37 +03:00
|
|
|
rb_trace_arg_t *prev_trace_arg = ec->trace_arg;
|
2018-11-26 21:16:39 +03:00
|
|
|
|
|
|
|
ec->trace_arg = trace_arg;
|
|
|
|
/* only global hooks */
|
|
|
|
exec_hooks_unprotected(ec, rb_vm_global_hooks(ec), trace_arg);
|
|
|
|
ec->trace_arg = prev_trace_arg;
|
2013-11-26 13:42:04 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2017-10-29 16:17:37 +03:00
|
|
|
if (ec->trace_arg == NULL && /* check reentrant */
|
2013-11-26 13:42:04 +04:00
|
|
|
trace_arg->self != rb_mRubyVMFrozenCore /* skip special methods. TODO: remove it. */) {
|
2017-10-29 16:17:37 +03:00
|
|
|
const VALUE errinfo = ec->errinfo;
|
|
|
|
const VALUE old_recursive = ec->local_storage_recursive_hash;
|
2013-11-26 13:42:04 +04:00
|
|
|
int state = 0;
|
2014-11-27 04:56:38 +03:00
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
/* setup */
|
2017-10-29 16:17:37 +03:00
|
|
|
ec->local_storage_recursive_hash = ec->local_storage_recursive_hash_for_trace;
|
|
|
|
ec->errinfo = Qnil;
|
|
|
|
ec->trace_arg = trace_arg;
|
2014-12-10 22:35:07 +03:00
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
/* kick hooks */
|
|
|
|
if ((state = exec_hooks_protected(ec, hooks, trace_arg)) == TAG_NONE) {
|
|
|
|
ec->errinfo = errinfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* cleanup */
|
|
|
|
ec->trace_arg = NULL;
|
2017-10-29 16:17:37 +03:00
|
|
|
ec->local_storage_recursive_hash_for_trace = ec->local_storage_recursive_hash;
|
|
|
|
ec->local_storage_recursive_hash = old_recursive;
|
2013-11-26 13:42:04 +04:00
|
|
|
|
|
|
|
if (state) {
|
|
|
|
if (pop_p) {
|
2017-10-29 16:17:37 +03:00
|
|
|
if (VM_FRAME_FINISHED_P(ec->cfp)) {
|
|
|
|
ec->tag = ec->tag->prev;
|
2013-11-26 13:42:04 +04:00
|
|
|
}
|
2017-10-29 16:17:37 +03:00
|
|
|
rb_vm_pop_frame(ec);
|
2013-01-07 10:24:46 +04:00
|
|
|
}
|
2017-10-29 16:17:37 +03:00
|
|
|
EC_JUMP_TAG(ec, state);
|
2013-01-07 10:24:46 +04:00
|
|
|
}
|
2012-12-11 22:45:43 +04:00
|
|
|
}
|
2012-08-16 15:41:24 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2012-08-16 15:41:24 +04:00
|
|
|
volatile int raised;
|
2019-06-08 13:46:32 +03:00
|
|
|
volatile VALUE result = Qnil;
|
2017-12-01 15:00:10 +03:00
|
|
|
rb_execution_context_t *const ec = GET_EC();
|
|
|
|
rb_vm_t *const vm = rb_ec_vm_ptr(ec);
|
2017-06-23 10:25:52 +03:00
|
|
|
enum ruby_tag_type state;
|
2012-12-21 13:33:44 +04:00
|
|
|
rb_trace_arg_t dummy_trace_arg;
|
2013-12-04 08:05:15 +04:00
|
|
|
dummy_trace_arg.event = 0;
|
2012-12-21 13:33:44 +04:00
|
|
|
|
2017-12-01 15:00:09 +03:00
|
|
|
if (!ec->trace_arg) {
|
|
|
|
ec->trace_arg = &dummy_trace_arg;
|
2017-11-16 05:47:58 +03:00
|
|
|
}
|
2012-08-16 15:41:24 +04:00
|
|
|
|
2017-11-07 07:01:13 +03:00
|
|
|
raised = rb_ec_reset_raised(ec);
|
2012-08-16 15:41:24 +04:00
|
|
|
|
2017-11-07 07:01:13 +03:00
|
|
|
EC_PUSH_TAG(ec);
|
2017-12-01 15:00:10 +03:00
|
|
|
if (LIKELY((state = EC_EXEC_TAG()) == TAG_NONE)) {
|
2012-08-16 15:41:24 +04:00
|
|
|
result = (*func)(arg);
|
|
|
|
}
|
2017-12-01 15:00:10 +03:00
|
|
|
else {
|
|
|
|
(void)*&vm; /* suppress "clobbered" warning */
|
|
|
|
}
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_POP_TAG();
|
2012-08-16 15:41:24 +04:00
|
|
|
|
|
|
|
if (raised) {
|
2017-11-07 07:01:13 +03:00
|
|
|
rb_ec_reset_raised(ec);
|
2012-08-16 15:41:24 +04:00
|
|
|
}
|
2012-12-21 13:33:44 +04:00
|
|
|
|
2017-12-01 15:00:09 +03:00
|
|
|
if (ec->trace_arg == &dummy_trace_arg) {
|
|
|
|
ec->trace_arg = NULL;
|
2017-11-16 05:47:58 +03:00
|
|
|
}
|
2012-08-16 15:41:24 +04:00
|
|
|
|
|
|
|
if (state) {
|
2017-08-02 06:02:22 +03:00
|
|
|
#if defined RUBY_USE_SETJMPEX && RUBY_USE_SETJMPEX
|
|
|
|
RB_GC_GUARD(result);
|
|
|
|
#endif
|
2017-11-07 07:01:13 +03:00
|
|
|
EC_JUMP_TAG(ec, state);
|
2012-08-16 15:41:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALUE klass);
|
|
|
|
|
|
|
|
/* (2-1) set_trace_func (old API) */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* set_trace_func(proc) -> proc
|
|
|
|
* set_trace_func(nil) -> nil
|
|
|
|
*
|
|
|
|
* Establishes _proc_ as the handler for tracing, or disables
|
2012-11-30 10:15:59 +04:00
|
|
|
* tracing if the parameter is +nil+.
|
|
|
|
*
|
2013-02-09 08:14:09 +04:00
|
|
|
* *Note:* this method is obsolete, please use TracePoint instead.
|
|
|
|
*
|
2012-11-30 10:15:59 +04:00
|
|
|
* _proc_ takes up to six parameters:
|
|
|
|
*
|
|
|
|
* * an event name
|
|
|
|
* * a filename
|
|
|
|
* * a line number
|
|
|
|
* * an object id
|
|
|
|
* * a binding
|
|
|
|
* * the name of a class
|
|
|
|
*
|
|
|
|
* _proc_ is invoked whenever an event occurs.
|
|
|
|
*
|
|
|
|
* Events are:
|
|
|
|
*
|
|
|
|
* +c-call+:: call a C-language routine
|
|
|
|
* +c-return+:: return from a C-language routine
|
|
|
|
* +call+:: call a Ruby method
|
2016-04-30 05:18:00 +03:00
|
|
|
* +class+:: start a class or module definition
|
|
|
|
* +end+:: finish a class or module definition
|
2012-11-30 10:15:59 +04:00
|
|
|
* +line+:: execute code on a new line
|
|
|
|
* +raise+:: raise an exception
|
|
|
|
* +return+:: return from a Ruby method
|
|
|
|
*
|
|
|
|
* Tracing is disabled within the context of _proc_.
|
2012-08-15 08:39:10 +04:00
|
|
|
*
|
|
|
|
* class Test
|
|
|
|
* def test
|
|
|
|
* a = 1
|
|
|
|
* b = 2
|
|
|
|
* end
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* set_trace_func proc { |event, file, line, id, binding, classname|
|
|
|
|
* printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
|
|
|
|
* }
|
|
|
|
* t = Test.new
|
|
|
|
* t.test
|
|
|
|
*
|
|
|
|
* line prog.rb:11 false
|
|
|
|
* c-call prog.rb:11 new Class
|
|
|
|
* c-call prog.rb:11 initialize Object
|
|
|
|
* c-return prog.rb:11 initialize Object
|
|
|
|
* c-return prog.rb:11 new Class
|
|
|
|
* line prog.rb:12 false
|
|
|
|
* call prog.rb:2 test Test
|
|
|
|
* line prog.rb:3 test Test
|
|
|
|
* line prog.rb:4 test Test
|
|
|
|
* return prog.rb:4 test Test
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
set_trace_func(VALUE obj, VALUE trace)
|
|
|
|
{
|
|
|
|
rb_remove_event_hook(call_trace_func);
|
|
|
|
|
|
|
|
if (NIL_P(trace)) {
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!rb_obj_is_proc(trace)) {
|
|
|
|
rb_raise(rb_eTypeError, "trace_func needs to be Proc");
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_add_event_hook(call_trace_func, RUBY_EVENT_ALL, trace);
|
|
|
|
return trace;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-11-15 16:21:24 +03:00
|
|
|
thread_add_trace_func(rb_execution_context_t *ec, rb_thread_t *filter_th, VALUE trace)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
|
|
|
if (!rb_obj_is_proc(trace)) {
|
|
|
|
rb_raise(rb_eTypeError, "trace_func needs to be Proc");
|
|
|
|
}
|
|
|
|
|
2017-11-15 16:21:24 +03:00
|
|
|
rb_threadptr_add_event_hook(ec, filter_th, call_trace_func, RUBY_EVENT_ALL, trace, RUBY_EVENT_HOOK_FLAG_SAFE);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* thr.add_trace_func(proc) -> proc
|
|
|
|
*
|
|
|
|
* Adds _proc_ as a handler for tracing.
|
2013-02-09 08:14:09 +04:00
|
|
|
*
|
|
|
|
* See Thread#set_trace_func and Kernel#set_trace_func.
|
2012-08-15 08:39:10 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
thread_add_trace_func_m(VALUE obj, VALUE trace)
|
|
|
|
{
|
2017-11-15 16:21:24 +03:00
|
|
|
thread_add_trace_func(GET_EC(), rb_thread_ptr(obj), trace);
|
2012-08-15 08:39:10 +04:00
|
|
|
return trace;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* thr.set_trace_func(proc) -> proc
|
|
|
|
* thr.set_trace_func(nil) -> nil
|
|
|
|
*
|
|
|
|
* Establishes _proc_ on _thr_ as the handler for tracing, or
|
|
|
|
* disables tracing if the parameter is +nil+.
|
2013-02-09 08:14:09 +04:00
|
|
|
*
|
|
|
|
* See Kernel#set_trace_func.
|
2012-08-15 08:39:10 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2017-06-28 07:49:30 +03:00
|
|
|
thread_set_trace_func_m(VALUE target_thread, VALUE trace)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2017-11-15 16:21:24 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *target_th = rb_thread_ptr(target_thread);
|
2013-01-29 11:50:27 +04:00
|
|
|
|
2017-11-15 16:21:24 +03:00
|
|
|
rb_threadptr_remove_event_hook(ec, target_th, call_trace_func, Qundef);
|
2012-08-15 08:39:10 +04:00
|
|
|
|
|
|
|
if (NIL_P(trace)) {
|
|
|
|
return Qnil;
|
|
|
|
}
|
2017-06-28 07:49:30 +03:00
|
|
|
else {
|
2017-11-15 16:21:24 +03:00
|
|
|
thread_add_trace_func(ec, target_th, trace);
|
2017-06-28 07:49:30 +03:00
|
|
|
return trace;
|
|
|
|
}
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
get_event_name(rb_event_flag_t event)
|
|
|
|
{
|
|
|
|
switch (event) {
|
2012-08-22 09:12:31 +04:00
|
|
|
case RUBY_EVENT_LINE: return "line";
|
|
|
|
case RUBY_EVENT_CLASS: return "class";
|
|
|
|
case RUBY_EVENT_END: return "end";
|
|
|
|
case RUBY_EVENT_CALL: return "call";
|
|
|
|
case RUBY_EVENT_RETURN: return "return";
|
|
|
|
case RUBY_EVENT_C_CALL: return "c-call";
|
|
|
|
case RUBY_EVENT_C_RETURN: return "c-return";
|
|
|
|
case RUBY_EVENT_RAISE: return "raise";
|
2012-08-15 08:39:10 +04:00
|
|
|
default:
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-22 09:12:31 +04:00
|
|
|
static ID
|
|
|
|
get_event_id(rb_event_flag_t event)
|
|
|
|
{
|
|
|
|
ID id;
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
#define C(name, NAME) case RUBY_EVENT_##NAME: CONST_ID(id, #name); return id;
|
|
|
|
C(line, LINE);
|
|
|
|
C(class, CLASS);
|
|
|
|
C(end, END);
|
|
|
|
C(call, CALL);
|
|
|
|
C(return, RETURN);
|
|
|
|
C(c_call, C_CALL);
|
|
|
|
C(c_return, C_RETURN);
|
|
|
|
C(raise, RAISE);
|
2012-11-30 02:28:16 +04:00
|
|
|
C(b_call, B_CALL);
|
|
|
|
C(b_return, B_RETURN);
|
|
|
|
C(thread_begin, THREAD_BEGIN);
|
|
|
|
C(thread_end, THREAD_END);
|
2015-08-21 12:51:01 +03:00
|
|
|
C(fiber_switch, FIBER_SWITCH);
|
2018-12-06 16:42:32 +03:00
|
|
|
C(script_compiled, SCRIPT_COMPILED);
|
2012-08-22 09:12:31 +04:00
|
|
|
#undef C
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-14 15:58:36 +03:00
|
|
|
static void
|
|
|
|
get_path_and_lineno(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, rb_event_flag_t event, VALUE *pathp, int *linep)
|
|
|
|
{
|
|
|
|
cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp);
|
|
|
|
|
|
|
|
if (cfp) {
|
|
|
|
const rb_iseq_t *iseq = cfp->iseq;
|
|
|
|
*pathp = rb_iseq_path(iseq);
|
|
|
|
|
|
|
|
if (event & (RUBY_EVENT_CLASS |
|
|
|
|
RUBY_EVENT_CALL |
|
|
|
|
RUBY_EVENT_B_CALL)) {
|
|
|
|
*linep = FIX2INT(rb_iseq_first_lineno(iseq));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*linep = rb_vm_get_sourceline(cfp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*pathp = Qnil;
|
|
|
|
*linep = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
static void
|
|
|
|
call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
|
2012-08-15 08:39:10 +04:00
|
|
|
{
|
2015-10-31 04:02:26 +03:00
|
|
|
int line;
|
2017-11-14 15:58:36 +03:00
|
|
|
VALUE filename;
|
2012-08-16 15:41:24 +04:00
|
|
|
VALUE eventname = rb_str_new2(get_event_name(event));
|
2012-08-15 08:39:10 +04:00
|
|
|
VALUE argv[6];
|
2017-10-28 14:11:17 +03:00
|
|
|
const rb_execution_context_t *ec = GET_EC();
|
2012-08-15 08:39:10 +04:00
|
|
|
|
2017-11-14 15:58:36 +03:00
|
|
|
get_path_and_lineno(ec, ec->cfp, event, &filename, &line);
|
|
|
|
|
2012-08-17 06:13:31 +04:00
|
|
|
if (!klass) {
|
2017-10-28 14:11:17 +03:00
|
|
|
rb_ec_frame_method_id_and_class(ec, &id, 0, &klass);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
2012-08-16 15:41:24 +04:00
|
|
|
|
2012-08-15 08:39:10 +04:00
|
|
|
if (klass) {
|
|
|
|
if (RB_TYPE_P(klass, T_ICLASS)) {
|
|
|
|
klass = RBASIC(klass)->klass;
|
|
|
|
}
|
|
|
|
else if (FL_TEST(klass, FL_SINGLETON)) {
|
2013-05-02 11:54:17 +04:00
|
|
|
klass = rb_ivar_get(klass, id__attached__);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argv[0] = eventname;
|
|
|
|
argv[1] = filename;
|
|
|
|
argv[2] = INT2FIX(line);
|
|
|
|
argv[3] = id ? ID2SYM(id) : Qnil;
|
2017-11-14 15:58:36 +03:00
|
|
|
argv[4] = (self && (filename != Qnil)) ? rb_binding_new() : Qnil;
|
2012-08-15 08:39:10 +04:00
|
|
|
argv[5] = klass ? klass : Qnil;
|
|
|
|
|
2012-08-16 15:41:24 +04:00
|
|
|
rb_proc_call_with_block(proc, 6, argv, Qnil);
|
2012-08-15 08:39:10 +04:00
|
|
|
}
|
|
|
|
|
2012-08-22 09:12:31 +04:00
|
|
|
/* (2-2) TracePoint API */
|
2012-08-15 08:39:10 +04:00
|
|
|
|
2012-08-22 09:12:31 +04:00
|
|
|
static VALUE rb_cTracePoint;
|
|
|
|
|
|
|
|
typedef struct rb_tp_struct {
|
|
|
|
rb_event_flag_t events;
|
2014-10-03 00:59:40 +04:00
|
|
|
int tracing; /* bool */
|
2012-08-22 09:12:31 +04:00
|
|
|
rb_thread_t *target_th;
|
2018-11-26 21:16:39 +03:00
|
|
|
VALUE local_target_set; /* Hash: target ->
|
|
|
|
* Qtrue (if target is iseq) or
|
|
|
|
* Qfalse (if target is bmethod)
|
|
|
|
*/
|
2012-11-20 16:57:49 +04:00
|
|
|
void (*func)(VALUE tpval, void *data);
|
|
|
|
void *data;
|
2012-08-22 09:12:31 +04:00
|
|
|
VALUE proc;
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE self;
|
2012-08-22 09:12:31 +04:00
|
|
|
} rb_tp_t;
|
|
|
|
|
|
|
|
static void
|
|
|
|
tp_mark(void *ptr)
|
|
|
|
{
|
2015-07-15 11:29:22 +03:00
|
|
|
rb_tp_t *tp = ptr;
|
|
|
|
rb_gc_mark(tp->proc);
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_gc_mark(tp->local_target_set);
|
2015-07-15 11:29:22 +03:00
|
|
|
if (tp->target_th) rb_gc_mark(tp->target_th->self);
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
tp_memsize(const void *ptr)
|
|
|
|
{
|
|
|
|
return sizeof(rb_tp_t);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const rb_data_type_t tp_data_type = {
|
|
|
|
"tracepoint",
|
2013-10-29 13:47:06 +04:00
|
|
|
{tp_mark, RUBY_TYPED_NEVER_FREE, tp_memsize,},
|
2014-12-01 09:38:04 +03:00
|
|
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
2012-08-22 09:12:31 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
tp_alloc(VALUE klass)
|
|
|
|
{
|
|
|
|
rb_tp_t *tp;
|
|
|
|
return TypedData_Make_Struct(klass, rb_tp_t, &tp_data_type, tp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static rb_event_flag_t
|
|
|
|
symbol2event_flag(VALUE v)
|
|
|
|
{
|
2015-04-26 02:45:02 +03:00
|
|
|
ID id;
|
2017-10-26 10:23:23 +03:00
|
|
|
VALUE sym = rb_to_symbol_type(v);
|
2015-04-26 02:55:34 +03:00
|
|
|
const rb_event_flag_t RUBY_EVENT_A_CALL =
|
|
|
|
RUBY_EVENT_CALL | RUBY_EVENT_B_CALL | RUBY_EVENT_C_CALL;
|
|
|
|
const rb_event_flag_t RUBY_EVENT_A_RETURN =
|
|
|
|
RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN | RUBY_EVENT_C_RETURN;
|
2012-08-22 09:12:31 +04:00
|
|
|
|
|
|
|
#define C(name, NAME) CONST_ID(id, #name); if (sym == ID2SYM(id)) return RUBY_EVENT_##NAME
|
|
|
|
C(line, LINE);
|
|
|
|
C(class, CLASS);
|
|
|
|
C(end, END);
|
|
|
|
C(call, CALL);
|
|
|
|
C(return, RETURN);
|
|
|
|
C(c_call, C_CALL);
|
|
|
|
C(c_return, C_RETURN);
|
|
|
|
C(raise, RAISE);
|
2012-11-30 02:28:16 +04:00
|
|
|
C(b_call, B_CALL);
|
|
|
|
C(b_return, B_RETURN);
|
|
|
|
C(thread_begin, THREAD_BEGIN);
|
|
|
|
C(thread_end, THREAD_END);
|
2015-08-21 12:51:01 +03:00
|
|
|
C(fiber_switch, FIBER_SWITCH);
|
2018-12-06 16:42:32 +03:00
|
|
|
C(script_compiled, SCRIPT_COMPILED);
|
|
|
|
|
|
|
|
/* joke */
|
2015-04-26 02:55:34 +03:00
|
|
|
C(a_call, A_CALL);
|
|
|
|
C(a_return, A_RETURN);
|
2012-08-22 09:12:31 +04:00
|
|
|
#undef C
|
2014-07-30 17:17:45 +04:00
|
|
|
rb_raise(rb_eArgError, "unknown event: %"PRIsVALUE, rb_sym2str(sym));
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static rb_tp_t *
|
|
|
|
tpptr(VALUE tpval)
|
|
|
|
{
|
|
|
|
rb_tp_t *tp;
|
|
|
|
TypedData_Get_Struct(tpval, rb_tp_t, &tp_data_type, tp);
|
|
|
|
return tp;
|
|
|
|
}
|
|
|
|
|
2012-12-21 13:33:44 +04:00
|
|
|
static rb_trace_arg_t *
|
|
|
|
get_trace_arg(void)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
2017-10-26 17:44:09 +03:00
|
|
|
rb_trace_arg_t *trace_arg = GET_EC()->trace_arg;
|
2012-12-21 13:33:44 +04:00
|
|
|
if (trace_arg == 0) {
|
2012-08-22 09:12:31 +04:00
|
|
|
rb_raise(rb_eRuntimeError, "access from outside");
|
|
|
|
}
|
2012-12-21 13:33:44 +04:00
|
|
|
return trace_arg;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
2012-11-29 10:43:31 +04:00
|
|
|
struct rb_trace_arg_struct *
|
|
|
|
rb_tracearg_from_tracepoint(VALUE tpval)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
2012-12-21 13:33:44 +04:00
|
|
|
return get_trace_arg();
|
2012-11-29 10:43:31 +04:00
|
|
|
}
|
|
|
|
|
2013-05-27 04:21:02 +04:00
|
|
|
rb_event_flag_t
|
|
|
|
rb_tracearg_event_flag(rb_trace_arg_t *trace_arg)
|
|
|
|
{
|
|
|
|
return trace_arg->event;
|
|
|
|
}
|
|
|
|
|
2012-11-29 10:43:31 +04:00
|
|
|
VALUE
|
|
|
|
rb_tracearg_event(rb_trace_arg_t *trace_arg)
|
|
|
|
{
|
|
|
|
return ID2SYM(get_event_id(trace_arg->event));
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
2012-11-27 03:01:45 +04:00
|
|
|
static void
|
2012-11-27 03:25:21 +04:00
|
|
|
fill_path_and_lineno(rb_trace_arg_t *trace_arg)
|
2012-11-27 03:01:45 +04:00
|
|
|
{
|
2012-11-27 03:25:21 +04:00
|
|
|
if (trace_arg->path == Qundef) {
|
2017-11-14 15:58:36 +03:00
|
|
|
get_path_and_lineno(trace_arg->ec, trace_arg->cfp, trace_arg->event, &trace_arg->path, &trace_arg->lineno);
|
2012-11-27 03:01:45 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_tracearg_lineno(rb_trace_arg_t *trace_arg)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
2012-11-29 10:43:31 +04:00
|
|
|
fill_path_and_lineno(trace_arg);
|
|
|
|
return INT2FIX(trace_arg->lineno);
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_tracearg_path(rb_trace_arg_t *trace_arg)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
2012-11-29 10:43:31 +04:00
|
|
|
fill_path_and_lineno(trace_arg);
|
|
|
|
return trace_arg->path;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fill_id_and_klass(rb_trace_arg_t *trace_arg)
|
|
|
|
{
|
2012-11-27 03:01:45 +04:00
|
|
|
if (!trace_arg->klass_solved) {
|
|
|
|
if (!trace_arg->klass) {
|
* vm_trace.c (tracepoint_attr_callee_id, rb_tracearg_callee_id):
add TracePoint#callee_id. [ruby-core:77241] [Feature #12747]
* cont.c, eval.c, gc.c, include/ruby/intern.h, insns.def, thread.c,
vm.c, vm_backtrace.c, vm_core.h, vm_eval.c, vm_insnhelper.c, vm_trace.c: ditto.
* test/ruby/test_settracefunc.rb: tests for above.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-11-05 16:15:27 +03:00
|
|
|
rb_vm_control_frame_id_and_class(trace_arg->cfp, &trace_arg->id, &trace_arg->called_id, &trace_arg->klass);
|
2012-11-27 03:01:45 +04:00
|
|
|
}
|
2012-08-22 09:12:31 +04:00
|
|
|
|
2012-11-27 03:01:45 +04:00
|
|
|
if (trace_arg->klass) {
|
|
|
|
if (RB_TYPE_P(trace_arg->klass, T_ICLASS)) {
|
|
|
|
trace_arg->klass = RBASIC(trace_arg->klass)->klass;
|
|
|
|
}
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
2012-11-27 03:01:45 +04:00
|
|
|
else {
|
|
|
|
trace_arg->klass = Qnil;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
2012-11-27 03:01:45 +04:00
|
|
|
|
|
|
|
trace_arg->klass_solved = 1;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-03 08:10:41 +03:00
|
|
|
VALUE
|
|
|
|
rb_tracearg_parameters(rb_trace_arg_t *trace_arg)
|
|
|
|
{
|
|
|
|
switch(trace_arg->event) {
|
|
|
|
case RUBY_EVENT_CALL:
|
|
|
|
case RUBY_EVENT_RETURN:
|
|
|
|
case RUBY_EVENT_B_CALL:
|
|
|
|
case RUBY_EVENT_B_RETURN: {
|
|
|
|
const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(trace_arg->ec, trace_arg->cfp);
|
|
|
|
if (cfp) {
|
|
|
|
int is_proc = 0;
|
|
|
|
if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_BLOCK && !VM_FRAME_LAMBDA_P(cfp)) {
|
|
|
|
is_proc = 1;
|
|
|
|
}
|
|
|
|
return rb_iseq_parameters(cfp->iseq, is_proc);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case RUBY_EVENT_C_CALL:
|
|
|
|
case RUBY_EVENT_C_RETURN: {
|
|
|
|
fill_id_and_klass(trace_arg);
|
|
|
|
if (trace_arg->klass && trace_arg->id) {
|
|
|
|
const rb_method_entry_t *me;
|
|
|
|
VALUE iclass = Qnil;
|
|
|
|
me = rb_method_entry_without_refinements(trace_arg->klass, trace_arg->id, &iclass);
|
|
|
|
return rb_unnamed_parameters(rb_method_entry_arity(me));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case RUBY_EVENT_RAISE:
|
|
|
|
case RUBY_EVENT_LINE:
|
|
|
|
case RUBY_EVENT_CLASS:
|
|
|
|
case RUBY_EVENT_END:
|
2018-12-06 16:42:32 +03:00
|
|
|
case RUBY_EVENT_SCRIPT_COMPILED:
|
2018-06-03 08:10:41 +03:00
|
|
|
rb_raise(rb_eRuntimeError, "not supported by this event");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_tracearg_method_id(rb_trace_arg_t *trace_arg)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
2012-11-29 10:43:31 +04:00
|
|
|
fill_id_and_klass(trace_arg);
|
|
|
|
return trace_arg->id ? ID2SYM(trace_arg->id) : Qnil;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
* vm_trace.c (tracepoint_attr_callee_id, rb_tracearg_callee_id):
add TracePoint#callee_id. [ruby-core:77241] [Feature #12747]
* cont.c, eval.c, gc.c, include/ruby/intern.h, insns.def, thread.c,
vm.c, vm_backtrace.c, vm_core.h, vm_eval.c, vm_insnhelper.c, vm_trace.c: ditto.
* test/ruby/test_settracefunc.rb: tests for above.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-11-05 16:15:27 +03:00
|
|
|
VALUE
|
|
|
|
rb_tracearg_callee_id(rb_trace_arg_t *trace_arg)
|
|
|
|
{
|
|
|
|
fill_id_and_klass(trace_arg);
|
|
|
|
return trace_arg->called_id ? ID2SYM(trace_arg->called_id) : Qnil;
|
|
|
|
}
|
|
|
|
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_tracearg_defined_class(rb_trace_arg_t *trace_arg)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
2012-11-29 10:43:31 +04:00
|
|
|
fill_id_and_klass(trace_arg);
|
|
|
|
return trace_arg->klass;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_tracearg_binding(rb_trace_arg_t *trace_arg)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
|
|
|
rb_control_frame_t *cfp;
|
2017-10-29 16:17:37 +03:00
|
|
|
cfp = rb_vm_get_binding_creatable_next_cfp(trace_arg->ec, trace_arg->cfp);
|
2013-02-05 10:04:59 +04:00
|
|
|
|
2012-08-22 09:12:31 +04:00
|
|
|
if (cfp) {
|
2017-10-29 16:17:37 +03:00
|
|
|
return rb_vm_make_binding(trace_arg->ec, cfp);
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_tracearg_self(rb_trace_arg_t *trace_arg)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
2012-11-29 10:43:31 +04:00
|
|
|
return trace_arg->self;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_tracearg_return_value(rb_trace_arg_t *trace_arg)
|
2012-11-20 13:48:24 +04:00
|
|
|
{
|
2012-11-30 02:28:16 +04:00
|
|
|
if (trace_arg->event & (RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN | RUBY_EVENT_B_RETURN)) {
|
2012-11-20 13:48:24 +04:00
|
|
|
/* ok */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_raise(rb_eRuntimeError, "not supported by this event");
|
|
|
|
}
|
2012-11-29 10:43:31 +04:00
|
|
|
if (trace_arg->data == Qundef) {
|
2018-01-31 03:19:33 +03:00
|
|
|
rb_bug("rb_tracearg_return_value: unreachable");
|
2012-11-24 08:12:30 +04:00
|
|
|
}
|
2012-11-29 10:43:31 +04:00
|
|
|
return trace_arg->data;
|
2012-11-20 13:48:24 +04:00
|
|
|
}
|
|
|
|
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg)
|
2012-11-20 13:48:24 +04:00
|
|
|
{
|
2012-11-29 10:43:31 +04:00
|
|
|
if (trace_arg->event & (RUBY_EVENT_RAISE)) {
|
2012-11-20 13:48:24 +04:00
|
|
|
/* ok */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_raise(rb_eRuntimeError, "not supported by this event");
|
|
|
|
}
|
2012-11-29 10:43:31 +04:00
|
|
|
if (trace_arg->data == Qundef) {
|
2018-01-31 03:19:33 +03:00
|
|
|
rb_bug("rb_tracearg_raised_exception: unreachable");
|
2013-05-27 04:21:02 +04:00
|
|
|
}
|
|
|
|
return trace_arg->data;
|
|
|
|
}
|
|
|
|
|
2018-12-06 16:42:32 +03:00
|
|
|
VALUE
|
2018-12-12 18:45:06 +03:00
|
|
|
rb_tracearg_eval_script(rb_trace_arg_t *trace_arg)
|
2018-12-06 16:42:32 +03:00
|
|
|
{
|
|
|
|
VALUE data = trace_arg->data;
|
|
|
|
|
|
|
|
if (trace_arg->event & (RUBY_EVENT_SCRIPT_COMPILED)) {
|
2018-12-06 16:42:35 +03:00
|
|
|
/* ok */
|
2018-12-06 16:42:32 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-12-06 16:42:35 +03:00
|
|
|
rb_raise(rb_eRuntimeError, "not supported by this event");
|
2018-12-06 16:42:32 +03:00
|
|
|
}
|
|
|
|
if (data == Qundef) {
|
|
|
|
rb_bug("rb_tracearg_raised_exception: unreachable");
|
|
|
|
}
|
|
|
|
if (rb_obj_is_iseq(data)) {
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VM_ASSERT(RB_TYPE_P(data, T_ARRAY));
|
|
|
|
/* [src, iseq] */
|
|
|
|
return RARRAY_AREF(data, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2018-12-12 18:45:06 +03:00
|
|
|
rb_tracearg_instruction_sequence(rb_trace_arg_t *trace_arg)
|
2018-12-06 16:42:32 +03:00
|
|
|
{
|
|
|
|
VALUE data = trace_arg->data;
|
|
|
|
|
|
|
|
if (trace_arg->event & (RUBY_EVENT_SCRIPT_COMPILED)) {
|
2018-12-06 16:42:35 +03:00
|
|
|
/* ok */
|
2018-12-06 16:42:32 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-12-06 16:42:35 +03:00
|
|
|
rb_raise(rb_eRuntimeError, "not supported by this event");
|
2018-12-06 16:42:32 +03:00
|
|
|
}
|
|
|
|
if (data == Qundef) {
|
|
|
|
rb_bug("rb_tracearg_raised_exception: unreachable");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rb_obj_is_iseq(data)) {
|
|
|
|
return rb_iseqw_new((const rb_iseq_t *)data);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VM_ASSERT(RB_TYPE_P(data, T_ARRAY));
|
|
|
|
VM_ASSERT(rb_obj_is_iseq(RARRAY_AREF(data, 1)));
|
|
|
|
|
|
|
|
/* [src, iseq] */
|
|
|
|
return rb_iseqw_new((const rb_iseq_t *)RARRAY_AREF(data, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-27 04:21:02 +04:00
|
|
|
VALUE
|
|
|
|
rb_tracearg_object(rb_trace_arg_t *trace_arg)
|
|
|
|
{
|
2013-05-27 14:28:25 +04:00
|
|
|
if (trace_arg->event & (RUBY_INTERNAL_EVENT_NEWOBJ | RUBY_INTERNAL_EVENT_FREEOBJ)) {
|
2013-05-27 04:21:02 +04:00
|
|
|
/* ok */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_raise(rb_eRuntimeError, "not supported by this event");
|
|
|
|
}
|
|
|
|
if (trace_arg->data == Qundef) {
|
2018-01-31 03:19:33 +03:00
|
|
|
rb_bug("rb_tracearg_object: unreachable");
|
2012-11-24 08:12:30 +04:00
|
|
|
}
|
2012-11-29 10:43:31 +04:00
|
|
|
return trace_arg->data;
|
|
|
|
}
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
|
|
|
* Type of event
|
|
|
|
*
|
2013-02-09 08:14:09 +04:00
|
|
|
* See TracePoint@Events for more information.
|
2012-11-30 13:25:24 +04:00
|
|
|
*/
|
2012-11-29 10:43:31 +04:00
|
|
|
static VALUE
|
|
|
|
tracepoint_attr_event(VALUE tpval)
|
|
|
|
{
|
2012-12-21 13:33:44 +04:00
|
|
|
return rb_tracearg_event(get_trace_arg());
|
2012-11-29 10:43:31 +04:00
|
|
|
}
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
|
|
|
* Line number of the event
|
|
|
|
*/
|
2012-11-29 10:43:31 +04:00
|
|
|
static VALUE
|
|
|
|
tracepoint_attr_lineno(VALUE tpval)
|
|
|
|
{
|
2012-12-21 13:33:44 +04:00
|
|
|
return rb_tracearg_lineno(get_trace_arg());
|
2012-11-29 10:43:31 +04:00
|
|
|
}
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
|
|
|
* Path of the file being run
|
|
|
|
*/
|
2012-11-29 10:43:31 +04:00
|
|
|
static VALUE
|
|
|
|
tracepoint_attr_path(VALUE tpval)
|
|
|
|
{
|
2012-12-21 13:33:44 +04:00
|
|
|
return rb_tracearg_path(get_trace_arg());
|
2012-11-29 10:43:31 +04:00
|
|
|
}
|
|
|
|
|
2018-06-03 08:10:41 +03:00
|
|
|
/*
|
2019-03-20 05:08:34 +03:00
|
|
|
* Return the parameters definition of the method or block that the
|
|
|
|
* current hook belongs to. Format is the same as for Method#parameters
|
2018-06-03 08:10:41 +03:00
|
|
|
*/
|
|
|
|
static VALUE
|
|
|
|
tracepoint_attr_parameters(VALUE tpval)
|
|
|
|
{
|
|
|
|
return rb_tracearg_parameters(get_trace_arg());
|
|
|
|
}
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
* vm_trace.c (tracepoint_attr_callee_id, rb_tracearg_callee_id):
add TracePoint#callee_id. [ruby-core:77241] [Feature #12747]
* cont.c, eval.c, gc.c, include/ruby/intern.h, insns.def, thread.c,
vm.c, vm_backtrace.c, vm_core.h, vm_eval.c, vm_insnhelper.c, vm_trace.c: ditto.
* test/ruby/test_settracefunc.rb: tests for above.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-11-05 16:15:27 +03:00
|
|
|
* Return the name at the definition of the method being called
|
2012-11-30 13:25:24 +04:00
|
|
|
*/
|
2012-11-29 10:43:31 +04:00
|
|
|
static VALUE
|
|
|
|
tracepoint_attr_method_id(VALUE tpval)
|
|
|
|
{
|
2012-12-21 13:33:44 +04:00
|
|
|
return rb_tracearg_method_id(get_trace_arg());
|
2012-11-29 10:43:31 +04:00
|
|
|
}
|
|
|
|
|
* vm_trace.c (tracepoint_attr_callee_id, rb_tracearg_callee_id):
add TracePoint#callee_id. [ruby-core:77241] [Feature #12747]
* cont.c, eval.c, gc.c, include/ruby/intern.h, insns.def, thread.c,
vm.c, vm_backtrace.c, vm_core.h, vm_eval.c, vm_insnhelper.c, vm_trace.c: ditto.
* test/ruby/test_settracefunc.rb: tests for above.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-11-05 16:15:27 +03:00
|
|
|
/*
|
|
|
|
* Return the called name of the method being called
|
|
|
|
*/
|
|
|
|
static VALUE
|
|
|
|
tracepoint_attr_callee_id(VALUE tpval)
|
|
|
|
{
|
|
|
|
return rb_tracearg_callee_id(get_trace_arg());
|
|
|
|
}
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
2012-12-18 08:18:06 +04:00
|
|
|
* Return class or module of the method being called.
|
2012-12-18 00:28:51 +04:00
|
|
|
*
|
2012-12-18 08:18:06 +04:00
|
|
|
* class C; def foo; end; end
|
|
|
|
* trace = TracePoint.new(:call) do |tp|
|
|
|
|
* p tp.defined_class #=> C
|
|
|
|
* end.enable do
|
|
|
|
* C.new.foo
|
|
|
|
* end
|
2012-12-18 00:28:51 +04:00
|
|
|
*
|
2012-12-18 08:18:06 +04:00
|
|
|
* If method is defined by a module, then that module is returned.
|
2012-12-18 00:28:51 +04:00
|
|
|
*
|
2012-12-18 08:18:06 +04:00
|
|
|
* module M; def foo; end; end
|
|
|
|
* class C; include M; end;
|
|
|
|
* trace = TracePoint.new(:call) do |tp|
|
|
|
|
* p tp.defined_class #=> M
|
|
|
|
* end.enable do
|
|
|
|
* C.new.foo
|
|
|
|
* end
|
2012-12-18 00:28:51 +04:00
|
|
|
*
|
2012-12-18 08:18:06 +04:00
|
|
|
* <b>Note:</b> #defined_class returns singleton class.
|
|
|
|
*
|
|
|
|
* 6th block parameter of Kernel#set_trace_func passes original class
|
|
|
|
* of attached by singleton class.
|
|
|
|
*
|
|
|
|
* <b>This is a difference between Kernel#set_trace_func and TracePoint.</b>
|
|
|
|
*
|
|
|
|
* class C; def self.foo; end; end
|
|
|
|
* trace = TracePoint.new(:call) do |tp|
|
|
|
|
* p tp.defined_class #=> #<Class:C>
|
|
|
|
* end.enable do
|
|
|
|
* C.foo
|
|
|
|
* end
|
2012-11-30 13:25:24 +04:00
|
|
|
*/
|
2012-11-29 10:43:31 +04:00
|
|
|
static VALUE
|
|
|
|
tracepoint_attr_defined_class(VALUE tpval)
|
|
|
|
{
|
2012-12-21 13:33:44 +04:00
|
|
|
return rb_tracearg_defined_class(get_trace_arg());
|
2012-11-29 10:43:31 +04:00
|
|
|
}
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
|
|
|
* Return the generated binding object from event
|
|
|
|
*/
|
2012-11-29 10:43:31 +04:00
|
|
|
static VALUE
|
|
|
|
tracepoint_attr_binding(VALUE tpval)
|
|
|
|
{
|
2012-12-21 13:33:44 +04:00
|
|
|
return rb_tracearg_binding(get_trace_arg());
|
2012-11-29 10:43:31 +04:00
|
|
|
}
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
|
|
|
* Return the trace object during event
|
|
|
|
*
|
|
|
|
* Same as TracePoint#binding:
|
|
|
|
* trace.binding.eval('self')
|
|
|
|
*/
|
2012-11-29 10:43:31 +04:00
|
|
|
static VALUE
|
|
|
|
tracepoint_attr_self(VALUE tpval)
|
|
|
|
{
|
2012-12-21 13:33:44 +04:00
|
|
|
return rb_tracearg_self(get_trace_arg());
|
2012-11-29 10:43:31 +04:00
|
|
|
}
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
2012-11-30 14:29:49 +04:00
|
|
|
* Return value from +:return+, +c_return+, and +b_return+ event
|
2012-11-30 13:25:24 +04:00
|
|
|
*/
|
2012-11-29 10:43:31 +04:00
|
|
|
static VALUE
|
|
|
|
tracepoint_attr_return_value(VALUE tpval)
|
|
|
|
{
|
2012-12-21 13:33:44 +04:00
|
|
|
return rb_tracearg_return_value(get_trace_arg());
|
2012-11-29 10:43:31 +04:00
|
|
|
}
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
|
|
|
* Value from exception raised on the +:raise+ event
|
|
|
|
*/
|
2012-11-29 10:43:31 +04:00
|
|
|
static VALUE
|
|
|
|
tracepoint_attr_raised_exception(VALUE tpval)
|
|
|
|
{
|
2012-12-21 13:33:44 +04:00
|
|
|
return rb_tracearg_raised_exception(get_trace_arg());
|
2012-11-20 13:48:24 +04:00
|
|
|
}
|
|
|
|
|
2018-12-06 16:42:32 +03:00
|
|
|
/*
|
|
|
|
* Compiled source code (String) on *eval methods on the +:script_compiled+ event.
|
|
|
|
* If loaded from a file, it will return nil.
|
|
|
|
*/
|
|
|
|
static VALUE
|
2018-12-12 18:45:06 +03:00
|
|
|
tracepoint_attr_eval_script(VALUE tpval)
|
2018-12-06 16:42:32 +03:00
|
|
|
{
|
2018-12-12 18:45:06 +03:00
|
|
|
return rb_tracearg_eval_script(get_trace_arg());
|
2018-12-06 16:42:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compiled instruction sequence represented by a RubyVM::InstructionSequence instance
|
|
|
|
* on the +:script_compiled+ event.
|
|
|
|
*
|
|
|
|
* Note that this method is MRI specific.
|
|
|
|
*/
|
|
|
|
static VALUE
|
2018-12-12 18:45:06 +03:00
|
|
|
tracepoint_attr_instruction_sequence(VALUE tpval)
|
2018-12-06 16:42:32 +03:00
|
|
|
{
|
2018-12-12 18:45:06 +03:00
|
|
|
return rb_tracearg_instruction_sequence(get_trace_arg());
|
2018-12-06 16:42:32 +03:00
|
|
|
}
|
|
|
|
|
2012-08-22 09:12:31 +04:00
|
|
|
static void
|
|
|
|
tp_call_trace(VALUE tpval, rb_trace_arg_t *trace_arg)
|
|
|
|
{
|
|
|
|
rb_tp_t *tp = tpptr(tpval);
|
|
|
|
|
2012-12-21 13:33:44 +04:00
|
|
|
if (tp->func) {
|
|
|
|
(*tp->func)(tpval, tp->data);
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
2012-12-21 13:33:44 +04:00
|
|
|
else {
|
|
|
|
rb_proc_call_with_block((VALUE)tp->proc, 1, &tpval, Qnil);
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
|
|
|
rb_tracepoint_enable(VALUE tpval)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
2013-01-29 11:51:00 +04:00
|
|
|
rb_tp_t *tp;
|
|
|
|
tp = tpptr(tpval);
|
2012-08-22 09:12:31 +04:00
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
if (tp->local_target_set != Qfalse) {
|
2019-03-28 02:56:14 +03:00
|
|
|
rb_raise(rb_eArgError, "can't nest-enable a targeting TracePoint");
|
2018-11-26 21:16:39 +03:00
|
|
|
}
|
|
|
|
|
2012-11-20 15:05:20 +04:00
|
|
|
if (tp->target_th) {
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_thread_add_event_hook2(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tp->events, tpval,
|
|
|
|
RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
else {
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_add_event_hook2((rb_event_hook_func_t)tp_call_trace, tp->events, tpval,
|
|
|
|
RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
2012-11-20 15:05:20 +04:00
|
|
|
tp->tracing = 1;
|
|
|
|
return Qundef;
|
|
|
|
}
|
2012-08-22 09:12:31 +04:00
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
static const rb_iseq_t *
|
|
|
|
iseq_of(VALUE target)
|
|
|
|
{
|
|
|
|
VALUE iseqv = rb_funcall(rb_cISeq, rb_intern("of"), 1, target);
|
|
|
|
if (NIL_P(iseqv)) {
|
|
|
|
rb_raise(rb_eArgError, "specified target is not supported");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return rb_iseqw_to_iseq(iseqv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const rb_method_definition_t *rb_method_def(VALUE method); /* proc.c */
|
|
|
|
|
|
|
|
static VALUE
|
2018-11-26 23:16:14 +03:00
|
|
|
rb_tracepoint_enable_for_target(VALUE tpval, VALUE target, VALUE target_line)
|
2018-11-26 21:16:39 +03:00
|
|
|
{
|
|
|
|
rb_tp_t *tp = tpptr(tpval);
|
|
|
|
const rb_iseq_t *iseq = iseq_of(target);
|
|
|
|
int n;
|
2018-11-26 23:16:14 +03:00
|
|
|
unsigned int line = 0;
|
2018-11-26 21:16:39 +03:00
|
|
|
|
|
|
|
if (tp->tracing > 0) {
|
2019-03-28 02:56:14 +03:00
|
|
|
rb_raise(rb_eArgError, "can't nest-enable a targeting TracePoint");
|
2018-11-26 21:16:39 +03:00
|
|
|
}
|
|
|
|
|
2018-11-26 23:16:14 +03:00
|
|
|
if (!NIL_P(target_line)) {
|
|
|
|
if ((tp->events & RUBY_EVENT_LINE) == 0) {
|
|
|
|
rb_raise(rb_eArgError, "target_line is specified, but line event is not specified");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
line = NUM2UINT(target_line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
VM_ASSERT(tp->local_target_set == Qfalse);
|
|
|
|
tp->local_target_set = rb_obj_hide(rb_ident_hash_new());
|
|
|
|
|
|
|
|
/* iseq */
|
2018-11-26 23:16:14 +03:00
|
|
|
n = rb_iseq_add_local_tracepoint_recursively(iseq, tp->events, tpval, line);
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_hash_aset(tp->local_target_set, (VALUE)iseq, Qtrue);
|
|
|
|
|
|
|
|
/* bmethod */
|
|
|
|
if (rb_obj_is_method(target)) {
|
|
|
|
rb_method_definition_t *def = (rb_method_definition_t *)rb_method_def(target);
|
|
|
|
if (def->type == VM_METHOD_TYPE_BMETHOD &&
|
|
|
|
(tp->events & (RUBY_EVENT_CALL | RUBY_EVENT_RETURN))) {
|
|
|
|
def->body.bmethod.hooks = ZALLOC(rb_hook_list_t);
|
2018-11-26 23:16:14 +03:00
|
|
|
rb_hook_list_connect_tracepoint(target, def->body.bmethod.hooks, tpval, 0);
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_hash_aset(tp->local_target_set, target, Qfalse);
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n == 0) {
|
|
|
|
rb_raise(rb_eArgError, "can not enable any hooks");
|
|
|
|
}
|
|
|
|
|
|
|
|
ruby_vm_event_local_num++;
|
|
|
|
|
|
|
|
tp->tracing = 1;
|
|
|
|
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
disable_local_event_iseq_i(VALUE target, VALUE iseq_p, VALUE tpval)
|
|
|
|
{
|
|
|
|
if (iseq_p) {
|
|
|
|
rb_iseq_remove_local_tracepoint_recursively((rb_iseq_t *)target, tpval);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* bmethod */
|
|
|
|
rb_method_definition_t *def = (rb_method_definition_t *)rb_method_def(target);
|
|
|
|
rb_hook_list_t *hooks = def->body.bmethod.hooks;
|
|
|
|
VM_ASSERT(hooks != NULL);
|
|
|
|
rb_hook_list_remove_tracepoint(hooks, tpval);
|
|
|
|
if (hooks->running == 0) {
|
|
|
|
rb_hook_list_free(def->body.bmethod.hooks);
|
|
|
|
}
|
|
|
|
def->body.bmethod.hooks = NULL;
|
|
|
|
}
|
|
|
|
return ST_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
|
|
|
rb_tracepoint_disable(VALUE tpval)
|
2012-11-20 15:05:20 +04:00
|
|
|
{
|
2013-01-29 11:51:00 +04:00
|
|
|
rb_tp_t *tp;
|
|
|
|
|
|
|
|
tp = tpptr(tpval);
|
2012-11-20 15:05:20 +04:00
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
if (tp->local_target_set) {
|
|
|
|
rb_hash_foreach(tp->local_target_set, disable_local_event_iseq_i, tpval);
|
|
|
|
tp->local_target_set = Qfalse;
|
|
|
|
ruby_vm_event_local_num--;
|
2012-11-20 15:05:20 +04:00
|
|
|
}
|
|
|
|
else {
|
2018-11-26 21:16:39 +03:00
|
|
|
if (tp->target_th) {
|
|
|
|
rb_thread_remove_event_hook_with_data(tp->target_th->self, (rb_event_hook_func_t)tp_call_trace, tpval);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_remove_event_hook_with_data((rb_event_hook_func_t)tp_call_trace, tpval);
|
|
|
|
}
|
2012-11-20 15:05:20 +04:00
|
|
|
}
|
|
|
|
tp->tracing = 0;
|
2018-12-29 19:44:09 +03:00
|
|
|
tp->target_th = NULL;
|
2012-11-20 15:05:20 +04:00
|
|
|
return Qundef;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
void
|
2018-11-26 23:16:14 +03:00
|
|
|
rb_hook_list_connect_tracepoint(VALUE target, rb_hook_list_t *list, VALUE tpval, unsigned int target_line)
|
2018-11-26 21:16:39 +03:00
|
|
|
{
|
|
|
|
rb_tp_t *tp = tpptr(tpval);
|
|
|
|
rb_event_hook_t *hook = alloc_event_hook((rb_event_hook_func_t)tp_call_trace, tp->events, tpval,
|
|
|
|
RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
|
2018-11-26 23:16:14 +03:00
|
|
|
hook->filter.target_line = target_line;
|
2018-11-26 21:16:39 +03:00
|
|
|
hook_list_connect(target, list, hook, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_hook_list_remove_tracepoint(rb_hook_list_t *list, VALUE tpval)
|
|
|
|
{
|
|
|
|
rb_event_hook_t *hook = list->hooks;
|
|
|
|
rb_event_flag_t events = 0;
|
|
|
|
|
|
|
|
while (hook) {
|
|
|
|
if (hook->data == tpval) {
|
|
|
|
hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED;
|
|
|
|
list->need_clean = TRUE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
events |= hook->events;
|
|
|
|
}
|
|
|
|
hook = hook->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
list->events = events;
|
|
|
|
}
|
|
|
|
|
2019-03-20 05:08:34 +03:00
|
|
|
/* :nodoc:
|
|
|
|
* Docs for the TracePointe#enable are in prelude.rb
|
2012-11-30 13:25:24 +04:00
|
|
|
*/
|
2012-08-22 09:12:31 +04:00
|
|
|
static VALUE
|
2018-12-29 19:44:09 +03:00
|
|
|
tracepoint_enable_m(VALUE tpval, VALUE target, VALUE target_line, VALUE target_thread)
|
2012-11-20 15:05:20 +04:00
|
|
|
{
|
|
|
|
rb_tp_t *tp = tpptr(tpval);
|
2012-12-06 07:13:50 +04:00
|
|
|
int previous_tracing = tp->tracing;
|
2018-11-26 21:16:39 +03:00
|
|
|
|
2018-12-29 19:44:09 +03:00
|
|
|
/* check target_thread */
|
|
|
|
if (RTEST(target_thread)) {
|
|
|
|
if (tp->target_th) {
|
|
|
|
rb_raise(rb_eArgError, "can not override target_thread filter");
|
|
|
|
}
|
|
|
|
tp->target_th = rb_thread_ptr(target_thread);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
tp->target_th = NULL;
|
|
|
|
}
|
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
if (NIL_P(target)) {
|
2018-11-26 23:16:14 +03:00
|
|
|
if (!NIL_P(target_line)) {
|
|
|
|
rb_raise(rb_eArgError, "only target_line is specified");
|
|
|
|
}
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_tracepoint_enable(tpval);
|
|
|
|
}
|
|
|
|
else {
|
2018-11-26 23:16:14 +03:00
|
|
|
rb_tracepoint_enable_for_target(tpval, target, target_line);
|
2018-11-26 21:16:39 +03:00
|
|
|
}
|
2012-12-06 07:13:50 +04:00
|
|
|
|
2012-11-20 15:05:20 +04:00
|
|
|
if (rb_block_given_p()) {
|
2017-10-26 09:58:09 +03:00
|
|
|
return rb_ensure(rb_yield, Qundef,
|
2012-12-06 07:13:50 +04:00
|
|
|
previous_tracing ? rb_tracepoint_enable : rb_tracepoint_disable,
|
|
|
|
tpval);
|
2012-11-20 15:05:20 +04:00
|
|
|
}
|
|
|
|
else {
|
2012-12-06 07:13:50 +04:00
|
|
|
return previous_tracing ? Qtrue : Qfalse;
|
2012-11-20 15:05:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2012-12-06 08:57:20 +04:00
|
|
|
* trace.disable -> true or false
|
2012-11-30 14:29:49 +04:00
|
|
|
* trace.disable { block } -> obj
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
* Deactivates the trace
|
|
|
|
*
|
2012-12-06 07:13:50 +04:00
|
|
|
* Return true if trace was enabled.
|
|
|
|
* Return false if trace was disabled.
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
* trace.enabled? #=> true
|
2017-10-28 04:05:17 +03:00
|
|
|
* trace.disable #=> true (previous status)
|
2012-11-30 13:25:24 +04:00
|
|
|
* trace.enabled? #=> false
|
2012-12-06 07:13:50 +04:00
|
|
|
* trace.disable #=> false
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
2012-11-30 14:29:49 +04:00
|
|
|
* If a block is given, the trace will only be disable within the scope of the
|
2012-12-06 07:13:50 +04:00
|
|
|
* block.
|
2012-11-30 14:29:49 +04:00
|
|
|
*
|
|
|
|
* trace.enabled?
|
|
|
|
* #=> true
|
|
|
|
*
|
|
|
|
* trace.disable do
|
|
|
|
* trace.enabled?
|
|
|
|
* # only disabled for this block
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* trace.enabled?
|
|
|
|
* #=> true
|
|
|
|
*
|
2012-12-06 07:13:50 +04:00
|
|
|
* Note: You cannot access event hooks within the block.
|
2012-11-30 14:29:49 +04:00
|
|
|
*
|
2012-12-06 07:13:50 +04:00
|
|
|
* trace.disable { p tp.lineno }
|
|
|
|
* #=> RuntimeError: access from outside
|
2012-11-30 13:25:24 +04:00
|
|
|
*/
|
2018-11-26 21:16:39 +03:00
|
|
|
|
2012-11-20 15:05:20 +04:00
|
|
|
static VALUE
|
2012-11-20 16:57:49 +04:00
|
|
|
tracepoint_disable_m(VALUE tpval)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
|
|
|
rb_tp_t *tp = tpptr(tpval);
|
2012-12-06 07:13:50 +04:00
|
|
|
int previous_tracing = tp->tracing;
|
|
|
|
|
2012-11-20 15:05:20 +04:00
|
|
|
if (rb_block_given_p()) {
|
2018-11-26 21:16:39 +03:00
|
|
|
if (tp->local_target_set != Qfalse) {
|
2019-03-28 02:56:14 +03:00
|
|
|
rb_raise(rb_eArgError, "can't disable a targeting TracePoint in a block");
|
2018-11-26 21:16:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
rb_tracepoint_disable(tpval);
|
|
|
|
return rb_ensure(rb_yield, Qundef,
|
2012-12-06 07:13:50 +04:00
|
|
|
previous_tracing ? rb_tracepoint_enable : rb_tracepoint_disable,
|
|
|
|
tpval);
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
else {
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_tracepoint_disable(tpval);
|
2012-12-06 07:13:50 +04:00
|
|
|
return previous_tracing ? Qtrue : Qfalse;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
2012-11-20 15:05:20 +04:00
|
|
|
}
|
2012-08-22 09:12:31 +04:00
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* trace.enabled? -> true or false
|
|
|
|
*
|
|
|
|
* The current status of the trace
|
|
|
|
*/
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
|
|
|
rb_tracepoint_enabled_p(VALUE tpval)
|
2012-11-20 15:05:20 +04:00
|
|
|
{
|
|
|
|
rb_tp_t *tp = tpptr(tpval);
|
|
|
|
return tp->tracing ? Qtrue : Qfalse;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2012-11-20 16:57:49 +04:00
|
|
|
tracepoint_new(VALUE klass, rb_thread_t *target_th, rb_event_flag_t events, void (func)(VALUE, void*), void *data, VALUE proc)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
2012-11-20 15:05:20 +04:00
|
|
|
VALUE tpval = tp_alloc(klass);
|
2012-08-22 09:12:31 +04:00
|
|
|
rb_tp_t *tp;
|
|
|
|
TypedData_Get_Struct(tpval, rb_tp_t, &tp_data_type, tp);
|
|
|
|
|
|
|
|
tp->proc = proc;
|
2012-11-20 16:57:49 +04:00
|
|
|
tp->func = func;
|
|
|
|
tp->data = data;
|
2012-08-22 09:12:31 +04:00
|
|
|
tp->events = events;
|
2012-11-20 16:57:49 +04:00
|
|
|
tp->self = tpval;
|
2012-08-22 09:12:31 +04:00
|
|
|
|
|
|
|
return tpval;
|
|
|
|
}
|
|
|
|
|
2015-04-15 11:40:25 +03:00
|
|
|
/*
|
|
|
|
* Creates a tracepoint by registering a callback function for one or more
|
|
|
|
* tracepoint events. Once the tracepoint is created, you can use
|
|
|
|
* rb_tracepoint_enable to enable the tracepoint.
|
|
|
|
*
|
|
|
|
* Parameters:
|
|
|
|
* 1. VALUE target_thval - Meant for picking the thread in which the tracepoint
|
|
|
|
* is to be created. However, current implementation ignore this parameter,
|
|
|
|
* tracepoint is created for all threads. Simply specify Qnil.
|
|
|
|
* 2. rb_event_flag_t events - Event(s) to listen to.
|
|
|
|
* 3. void (*func)(VALUE, void *) - A callback function.
|
|
|
|
* 4. void *data - Void pointer that will be passed to the callback function.
|
|
|
|
*
|
|
|
|
* When the callback function is called, it will be passed 2 parameters:
|
|
|
|
* 1)VALUE tpval - the TracePoint object from which trace args can be extracted.
|
|
|
|
* 2)void *data - A void pointer which helps to share scope with the callback function.
|
|
|
|
*
|
|
|
|
* It is important to note that you cannot register callbacks for normal events and internal events
|
|
|
|
* simultaneously because they are different purpose.
|
|
|
|
* You can use any Ruby APIs (calling methods and so on) on normal event hooks.
|
2015-04-15 12:44:38 +03:00
|
|
|
* However, in internal events, you can not use any Ruby APIs (even object creations).
|
2015-04-15 11:40:25 +03:00
|
|
|
* This is why we can't specify internal events by TracePoint directly.
|
|
|
|
* Limitations are MRI version specific.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ | RUBY_INTERNAL_EVENT_FREEOBJ, obj_event_i, data);
|
|
|
|
*
|
|
|
|
* In this example, a callback function obj_event_i will be registered for
|
|
|
|
* internal events RUBY_INTERNAL_EVENT_NEWOBJ and RUBY_INTERNAL_EVENT_FREEOBJ.
|
|
|
|
*/
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE
|
2012-12-21 15:22:54 +04:00
|
|
|
rb_tracepoint_new(VALUE target_thval, rb_event_flag_t events, void (*func)(VALUE, void *), void *data)
|
2012-11-20 16:57:49 +04:00
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *target_th = NULL;
|
|
|
|
|
2012-12-21 15:22:54 +04:00
|
|
|
if (RTEST(target_thval)) {
|
2017-06-28 07:49:30 +03:00
|
|
|
target_th = rb_thread_ptr(target_thval);
|
2012-12-21 15:22:54 +04:00
|
|
|
/* TODO: Test it!
|
|
|
|
* Warning: This function is not tested.
|
|
|
|
*/
|
2012-11-20 16:57:49 +04:00
|
|
|
}
|
|
|
|
return tracepoint_new(rb_cTracePoint, target_th, events, func, data, Qundef);
|
|
|
|
}
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* TracePoint.new(*events) { |obj| block } -> obj
|
|
|
|
*
|
|
|
|
* Returns a new TracePoint object, not enabled by default.
|
|
|
|
*
|
2017-11-08 19:43:23 +03:00
|
|
|
* Next, in order to activate the trace, you must use TracePoint#enable
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
* trace = TracePoint.new(:call) do |tp|
|
|
|
|
* p [tp.lineno, tp.defined_class, tp.method_id, tp.event]
|
|
|
|
* end
|
2013-08-05 17:25:02 +04:00
|
|
|
* #=> #<TracePoint:disabled>
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
* trace.enable
|
2013-08-05 17:25:02 +04:00
|
|
|
* #=> false
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
* puts "Hello, TracePoint!"
|
|
|
|
* # ...
|
|
|
|
* # [48, IRB::Notifier::AbstractNotifier, :printf, :call]
|
|
|
|
* # ...
|
|
|
|
*
|
2017-11-08 19:43:23 +03:00
|
|
|
* When you want to deactivate the trace, you must use TracePoint#disable
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
* trace.disable
|
|
|
|
*
|
2013-02-09 08:14:09 +04:00
|
|
|
* See TracePoint@Events for possible events and more information.
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
2017-11-10 01:04:47 +03:00
|
|
|
* A block must be given, otherwise an ArgumentError is raised.
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
* If the trace method isn't included in the given events filter, a
|
|
|
|
* RuntimeError is raised.
|
|
|
|
*
|
|
|
|
* TracePoint.trace(:line) do |tp|
|
|
|
|
* p tp.raised_exception
|
|
|
|
* end
|
|
|
|
* #=> RuntimeError: 'raised_exception' not supported by this event
|
2012-12-21 13:33:44 +04:00
|
|
|
*
|
|
|
|
* If the trace method is called outside block, a RuntimeError is raised.
|
|
|
|
*
|
|
|
|
* TracePoint.trace(:line) do |tp|
|
|
|
|
* $tp = tp
|
|
|
|
* end
|
2017-10-28 09:49:50 +03:00
|
|
|
* $tp.lineno #=> access from outside (RuntimeError)
|
2012-12-21 13:33:44 +04:00
|
|
|
*
|
|
|
|
* Access from other threads is also forbidden.
|
|
|
|
*
|
2012-11-30 13:25:24 +04:00
|
|
|
*/
|
2012-08-22 09:12:31 +04:00
|
|
|
static VALUE
|
2012-11-20 16:57:49 +04:00
|
|
|
tracepoint_new_s(int argc, VALUE *argv, VALUE self)
|
2012-08-22 09:12:31 +04:00
|
|
|
{
|
|
|
|
rb_event_flag_t events = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (argc > 0) {
|
|
|
|
for (i=0; i<argc; i++) {
|
|
|
|
events |= symbol2event_flag(argv[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2012-11-30 02:28:16 +04:00
|
|
|
events = RUBY_EVENT_TRACEPOINT_ALL;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!rb_block_given_p()) {
|
2017-11-10 01:04:47 +03:00
|
|
|
rb_raise(rb_eArgError, "must be called with a block");
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
|
|
|
|
2012-11-20 16:57:49 +04:00
|
|
|
return tracepoint_new(self, 0, events, 0, 0, rb_block_proc());
|
2012-11-20 15:05:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2012-11-20 16:57:49 +04:00
|
|
|
tracepoint_trace_s(int argc, VALUE *argv, VALUE self)
|
2012-11-20 15:05:20 +04:00
|
|
|
{
|
2012-11-20 16:57:49 +04:00
|
|
|
VALUE trace = tracepoint_new_s(argc, argv, self);
|
|
|
|
rb_tracepoint_enable(trace);
|
2012-11-20 15:05:20 +04:00
|
|
|
return trace;
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
2012-08-15 08:39:10 +04:00
|
|
|
|
2012-12-01 21:47:37 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* trace.inspect -> string
|
|
|
|
*
|
|
|
|
* Return a string containing a human-readable TracePoint
|
|
|
|
* status.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
tracepoint_inspect(VALUE self)
|
|
|
|
{
|
|
|
|
rb_tp_t *tp = tpptr(self);
|
2017-10-26 17:44:09 +03:00
|
|
|
rb_trace_arg_t *trace_arg = GET_EC()->trace_arg;
|
2012-12-01 21:47:37 +04:00
|
|
|
|
2012-12-21 13:33:44 +04:00
|
|
|
if (trace_arg) {
|
|
|
|
switch (trace_arg->event) {
|
2012-12-01 21:47:37 +04:00
|
|
|
case RUBY_EVENT_LINE:
|
|
|
|
{
|
2012-12-21 13:33:44 +04:00
|
|
|
VALUE sym = rb_tracearg_method_id(trace_arg);
|
2012-12-01 21:47:37 +04:00
|
|
|
if (NIL_P(sym))
|
2015-04-26 01:19:46 +03:00
|
|
|
goto default_inspect;
|
2012-12-01 21:47:37 +04:00
|
|
|
return rb_sprintf("#<TracePoint:%"PRIsVALUE"@%"PRIsVALUE":%d in `%"PRIsVALUE"'>",
|
2012-12-21 13:33:44 +04:00
|
|
|
rb_tracearg_event(trace_arg),
|
|
|
|
rb_tracearg_path(trace_arg),
|
|
|
|
FIX2INT(rb_tracearg_lineno(trace_arg)),
|
2012-12-01 21:47:37 +04:00
|
|
|
sym);
|
|
|
|
}
|
|
|
|
case RUBY_EVENT_CALL:
|
|
|
|
case RUBY_EVENT_C_CALL:
|
|
|
|
case RUBY_EVENT_RETURN:
|
|
|
|
case RUBY_EVENT_C_RETURN:
|
|
|
|
return rb_sprintf("#<TracePoint:%"PRIsVALUE" `%"PRIsVALUE"'@%"PRIsVALUE":%d>",
|
2012-12-21 13:33:44 +04:00
|
|
|
rb_tracearg_event(trace_arg),
|
|
|
|
rb_tracearg_method_id(trace_arg),
|
|
|
|
rb_tracearg_path(trace_arg),
|
|
|
|
FIX2INT(rb_tracearg_lineno(trace_arg)));
|
2012-12-01 21:47:37 +04:00
|
|
|
case RUBY_EVENT_THREAD_BEGIN:
|
|
|
|
case RUBY_EVENT_THREAD_END:
|
|
|
|
return rb_sprintf("#<TracePoint:%"PRIsVALUE" %"PRIsVALUE">",
|
2012-12-21 13:33:44 +04:00
|
|
|
rb_tracearg_event(trace_arg),
|
|
|
|
rb_tracearg_self(trace_arg));
|
2012-12-01 21:47:37 +04:00
|
|
|
default:
|
|
|
|
default_inspect:
|
|
|
|
return rb_sprintf("#<TracePoint:%"PRIsVALUE"@%"PRIsVALUE":%d>",
|
2012-12-21 13:33:44 +04:00
|
|
|
rb_tracearg_event(trace_arg),
|
|
|
|
rb_tracearg_path(trace_arg),
|
|
|
|
FIX2INT(rb_tracearg_lineno(trace_arg)));
|
2012-12-01 21:47:37 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return rb_sprintf("#<TracePoint:%s>", tp->tracing ? "enabled" : "disabled");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-13 13:01:21 +04:00
|
|
|
static void
|
|
|
|
tracepoint_stat_event_hooks(VALUE hash, VALUE key, rb_event_hook_t *hook)
|
|
|
|
{
|
|
|
|
int active = 0, deleted = 0;
|
|
|
|
|
|
|
|
while (hook) {
|
|
|
|
if (hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) {
|
|
|
|
deleted++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
active++;
|
|
|
|
}
|
|
|
|
hook = hook->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_hash_aset(hash, key, rb_ary_new3(2, INT2FIX(active), INT2FIX(deleted)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* TracePoint.stat -> obj
|
|
|
|
*
|
|
|
|
* Returns internal information of TracePoint.
|
|
|
|
*
|
|
|
|
* The contents of the returned value are implementation specific.
|
|
|
|
* It may be changed in future.
|
|
|
|
*
|
|
|
|
* This method is only for debugging TracePoint itself.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
tracepoint_stat_s(VALUE self)
|
|
|
|
{
|
|
|
|
rb_vm_t *vm = GET_VM();
|
|
|
|
VALUE stat = rb_hash_new();
|
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
tracepoint_stat_event_hooks(stat, vm->self, vm->global_hooks.hooks);
|
2014-06-13 13:01:21 +04:00
|
|
|
/* TODO: thread local hooks */
|
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2012-08-15 08:39:10 +04:00
|
|
|
/* This function is called from inits.c */
|
|
|
|
void
|
|
|
|
Init_vm_trace(void)
|
|
|
|
{
|
2012-08-22 09:12:31 +04:00
|
|
|
/* trace_func */
|
2012-08-15 08:39:10 +04:00
|
|
|
rb_define_global_function("set_trace_func", set_trace_func, 1);
|
|
|
|
rb_define_method(rb_cThread, "set_trace_func", thread_set_trace_func_m, 1);
|
|
|
|
rb_define_method(rb_cThread, "add_trace_func", thread_add_trace_func_m, 1);
|
|
|
|
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
|
|
|
* Document-class: TracePoint
|
|
|
|
*
|
|
|
|
* A class that provides the functionality of Kernel#set_trace_func in a
|
|
|
|
* nice Object-Oriented API.
|
|
|
|
*
|
2013-02-09 08:14:09 +04:00
|
|
|
* == Example
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
* We can use TracePoint to gather information specifically for exceptions:
|
|
|
|
*
|
|
|
|
* trace = TracePoint.new(:raise) do |tp|
|
|
|
|
* p [tp.lineno, tp.event, tp.raised_exception]
|
|
|
|
* end
|
2013-08-05 17:25:02 +04:00
|
|
|
* #=> #<TracePoint:disabled>
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
* trace.enable
|
2013-08-05 17:25:02 +04:00
|
|
|
* #=> false
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
* 0 / 0
|
|
|
|
* #=> [5, :raise, #<ZeroDivisionError: divided by 0>]
|
|
|
|
*
|
2013-02-09 08:14:09 +04:00
|
|
|
* == Events
|
|
|
|
*
|
|
|
|
* If you don't specify the type of events you want to listen for,
|
|
|
|
* TracePoint will include all available events.
|
|
|
|
*
|
|
|
|
* *Note* do not depend on current event set, as this list is subject to
|
|
|
|
* change. Instead, it is recommended you specify the type of events you
|
|
|
|
* want to use.
|
|
|
|
*
|
|
|
|
* To filter what is traced, you can pass any of the following as +events+:
|
|
|
|
*
|
|
|
|
* +:line+:: execute code on a new line
|
|
|
|
* +:class+:: start a class or module definition
|
|
|
|
* +:end+:: finish a class or module definition
|
|
|
|
* +:call+:: call a Ruby method
|
|
|
|
* +:return+:: return from a Ruby method
|
|
|
|
* +:c_call+:: call a C-language routine
|
|
|
|
* +:c_return+:: return from a C-language routine
|
|
|
|
* +:raise+:: raise an exception
|
|
|
|
* +:b_call+:: event hook at block entry
|
|
|
|
* +:b_return+:: event hook at block ending
|
|
|
|
* +:thread_begin+:: event hook at thread beginning
|
|
|
|
* +:thread_end+:: event hook at thread ending
|
2015-12-14 05:52:14 +03:00
|
|
|
* +:fiber_switch+:: event hook at fiber switch
|
2019-03-20 05:08:34 +03:00
|
|
|
* +:script_compiled+:: new Ruby code compiled (with +eval+, +load+ or +require+)
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
*/
|
2012-08-22 09:12:31 +04:00
|
|
|
rb_cTracePoint = rb_define_class("TracePoint", rb_cObject);
|
|
|
|
rb_undef_alloc_func(rb_cTracePoint);
|
2012-11-20 16:57:49 +04:00
|
|
|
rb_define_singleton_method(rb_cTracePoint, "new", tracepoint_new_s, -1);
|
2012-11-30 13:25:24 +04:00
|
|
|
/*
|
|
|
|
* Document-method: trace
|
|
|
|
*
|
|
|
|
* call-seq:
|
|
|
|
* TracePoint.trace(*events) { |obj| block } -> obj
|
|
|
|
*
|
|
|
|
* A convenience method for TracePoint.new, that activates the trace
|
|
|
|
* automatically.
|
|
|
|
*
|
|
|
|
* trace = TracePoint.trace(:call) { |tp| [tp.lineno, tp.event] }
|
2013-08-05 17:25:02 +04:00
|
|
|
* #=> #<TracePoint:enabled>
|
2012-11-30 13:25:24 +04:00
|
|
|
*
|
|
|
|
* trace.enabled? #=> true
|
|
|
|
*/
|
2012-11-20 16:57:49 +04:00
|
|
|
rb_define_singleton_method(rb_cTracePoint, "trace", tracepoint_trace_s, -1);
|
|
|
|
|
2018-12-29 19:44:09 +03:00
|
|
|
rb_define_method(rb_cTracePoint, "__enable", tracepoint_enable_m, 3);
|
2012-11-20 16:57:49 +04:00
|
|
|
rb_define_method(rb_cTracePoint, "disable", tracepoint_disable_m, 0);
|
|
|
|
rb_define_method(rb_cTracePoint, "enabled?", rb_tracepoint_enabled_p, 0);
|
|
|
|
|
2012-12-01 21:47:37 +04:00
|
|
|
rb_define_method(rb_cTracePoint, "inspect", tracepoint_inspect, 0);
|
|
|
|
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_define_method(rb_cTracePoint, "event", tracepoint_attr_event, 0);
|
|
|
|
rb_define_method(rb_cTracePoint, "lineno", tracepoint_attr_lineno, 0);
|
|
|
|
rb_define_method(rb_cTracePoint, "path", tracepoint_attr_path, 0);
|
2018-06-03 08:10:41 +03:00
|
|
|
rb_define_method(rb_cTracePoint, "parameters", tracepoint_attr_parameters, 0);
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_define_method(rb_cTracePoint, "method_id", tracepoint_attr_method_id, 0);
|
* vm_trace.c (tracepoint_attr_callee_id, rb_tracearg_callee_id):
add TracePoint#callee_id. [ruby-core:77241] [Feature #12747]
* cont.c, eval.c, gc.c, include/ruby/intern.h, insns.def, thread.c,
vm.c, vm_backtrace.c, vm_core.h, vm_eval.c, vm_insnhelper.c, vm_trace.c: ditto.
* test/ruby/test_settracefunc.rb: tests for above.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-11-05 16:15:27 +03:00
|
|
|
rb_define_method(rb_cTracePoint, "callee_id", tracepoint_attr_callee_id, 0);
|
2012-11-29 10:43:31 +04:00
|
|
|
rb_define_method(rb_cTracePoint, "defined_class", tracepoint_attr_defined_class, 0);
|
|
|
|
rb_define_method(rb_cTracePoint, "binding", tracepoint_attr_binding, 0);
|
|
|
|
rb_define_method(rb_cTracePoint, "self", tracepoint_attr_self, 0);
|
|
|
|
rb_define_method(rb_cTracePoint, "return_value", tracepoint_attr_return_value, 0);
|
|
|
|
rb_define_method(rb_cTracePoint, "raised_exception", tracepoint_attr_raised_exception, 0);
|
2018-12-12 18:45:06 +03:00
|
|
|
rb_define_method(rb_cTracePoint, "eval_script", tracepoint_attr_eval_script, 0);
|
|
|
|
rb_define_method(rb_cTracePoint, "instruction_sequence", tracepoint_attr_instruction_sequence, 0);
|
2013-10-10 08:56:32 +04:00
|
|
|
|
2014-06-13 13:01:21 +04:00
|
|
|
rb_define_singleton_method(rb_cTracePoint, "stat", tracepoint_stat_s, 0);
|
2012-08-22 09:12:31 +04:00
|
|
|
}
|
2012-11-29 10:43:31 +04:00
|
|
|
|
2018-10-23 06:47:45 +03:00
|
|
|
typedef struct rb_postponed_job_struct {
|
|
|
|
rb_postponed_job_func_t func;
|
|
|
|
void *data;
|
|
|
|
} rb_postponed_job_t;
|
|
|
|
|
2013-10-22 10:24:54 +04:00
|
|
|
#define MAX_POSTPONED_JOB 1000
|
|
|
|
#define MAX_POSTPONED_JOB_SPECIAL_ADDITION 24
|
2013-10-10 08:56:32 +04:00
|
|
|
|
2018-11-30 06:56:29 +03:00
|
|
|
struct rb_workqueue_job {
|
|
|
|
struct list_node jnode; /* <=> vm->workqueue */
|
|
|
|
rb_postponed_job_t job;
|
|
|
|
};
|
|
|
|
|
2018-11-08 08:35:46 +03:00
|
|
|
void
|
|
|
|
Init_vm_postponed_job(void)
|
2013-10-10 08:56:32 +04:00
|
|
|
{
|
|
|
|
rb_vm_t *vm = GET_VM();
|
2018-10-23 06:47:45 +03:00
|
|
|
vm->postponed_job_buffer = ALLOC_N(rb_postponed_job_t, MAX_POSTPONED_JOB);
|
|
|
|
vm->postponed_job_index = 0;
|
2018-11-30 06:56:29 +03:00
|
|
|
/* workqueue is initialized when VM locks are initialized */
|
2013-10-10 08:56:32 +04:00
|
|
|
}
|
|
|
|
|
2013-10-22 10:24:54 +04:00
|
|
|
enum postponed_job_register_result {
|
2018-05-17 06:48:27 +03:00
|
|
|
PJRR_SUCCESS = 0,
|
2018-10-23 06:47:45 +03:00
|
|
|
PJRR_FULL = 1,
|
|
|
|
PJRR_INTERRUPTED = 2
|
2013-10-22 10:24:54 +04:00
|
|
|
};
|
|
|
|
|
2018-11-30 06:56:29 +03:00
|
|
|
/* Async-signal-safe */
|
2013-10-22 10:24:54 +04:00
|
|
|
static enum postponed_job_register_result
|
2017-11-07 07:53:11 +03:00
|
|
|
postponed_job_register(rb_execution_context_t *ec, rb_vm_t *vm,
|
2018-10-23 06:47:48 +03:00
|
|
|
unsigned int flags, rb_postponed_job_func_t func, void *data, int max, int expected_index)
|
2013-05-27 01:30:44 +04:00
|
|
|
{
|
2018-10-23 06:47:45 +03:00
|
|
|
rb_postponed_job_t *pjob;
|
|
|
|
|
|
|
|
if (expected_index >= max) return PJRR_FULL; /* failed */
|
|
|
|
|
|
|
|
if (ATOMIC_CAS(vm->postponed_job_index, expected_index, expected_index+1) == expected_index) {
|
2018-10-23 06:47:48 +03:00
|
|
|
pjob = &vm->postponed_job_buffer[expected_index];
|
2018-10-23 06:47:45 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-10-23 06:47:48 +03:00
|
|
|
return PJRR_INTERRUPTED;
|
2018-10-23 06:47:45 +03:00
|
|
|
}
|
2013-10-10 08:56:32 +04:00
|
|
|
|
2018-10-23 06:47:45 +03:00
|
|
|
/* unused: pjob->flags = flags; */
|
|
|
|
pjob->func = func;
|
|
|
|
pjob->data = data;
|
2013-05-27 01:30:44 +04:00
|
|
|
|
2017-11-07 07:53:11 +03:00
|
|
|
RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(ec);
|
2013-10-10 08:56:32 +04:00
|
|
|
|
2018-05-17 06:48:27 +03:00
|
|
|
return PJRR_SUCCESS;
|
2013-10-22 10:24:54 +04:00
|
|
|
}
|
|
|
|
|
2018-10-23 06:47:45 +03:00
|
|
|
/*
|
|
|
|
* return 0 if job buffer is full
|
|
|
|
* Async-signal-safe
|
|
|
|
*/
|
2013-10-22 10:24:54 +04:00
|
|
|
int
|
|
|
|
rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data)
|
|
|
|
{
|
2017-11-07 07:53:11 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
rb_vm_t *vm = rb_ec_vm_ptr(ec);
|
2013-10-22 10:24:54 +04:00
|
|
|
|
2018-10-23 06:47:45 +03:00
|
|
|
begin:
|
|
|
|
switch (postponed_job_register(ec, vm, flags, func, data, MAX_POSTPONED_JOB, vm->postponed_job_index)) {
|
2018-05-17 06:48:27 +03:00
|
|
|
case PJRR_SUCCESS : return 1;
|
2013-10-22 10:24:54 +04:00
|
|
|
case PJRR_FULL : return 0;
|
2018-10-23 06:47:45 +03:00
|
|
|
case PJRR_INTERRUPTED: goto begin;
|
2013-10-22 10:24:54 +04:00
|
|
|
default: rb_bug("unreachable\n");
|
|
|
|
}
|
2013-05-27 01:30:44 +04:00
|
|
|
}
|
|
|
|
|
2018-10-23 06:47:45 +03:00
|
|
|
/*
|
|
|
|
* return 0 if job buffer is full
|
|
|
|
* Async-signal-safe
|
|
|
|
*/
|
2013-05-27 01:30:44 +04:00
|
|
|
int
|
|
|
|
rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data)
|
|
|
|
{
|
2017-11-07 07:53:11 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
rb_vm_t *vm = rb_ec_vm_ptr(ec);
|
2018-10-23 06:47:45 +03:00
|
|
|
rb_postponed_job_t *pjob;
|
|
|
|
int i, index;
|
|
|
|
|
|
|
|
begin:
|
|
|
|
index = vm->postponed_job_index;
|
|
|
|
for (i=0; i<index; i++) {
|
2018-10-23 06:47:48 +03:00
|
|
|
pjob = &vm->postponed_job_buffer[i];
|
|
|
|
if (pjob->func == func) {
|
|
|
|
RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(ec);
|
|
|
|
return 2;
|
|
|
|
}
|
2013-05-27 01:30:44 +04:00
|
|
|
}
|
2018-10-23 06:47:45 +03:00
|
|
|
switch (postponed_job_register(ec, vm, flags, func, data, MAX_POSTPONED_JOB + MAX_POSTPONED_JOB_SPECIAL_ADDITION, index)) {
|
2018-05-17 06:48:27 +03:00
|
|
|
case PJRR_SUCCESS : return 1;
|
2013-10-22 10:24:54 +04:00
|
|
|
case PJRR_FULL : return 0;
|
2018-10-23 06:47:45 +03:00
|
|
|
case PJRR_INTERRUPTED: goto begin;
|
2013-10-22 10:24:54 +04:00
|
|
|
default: rb_bug("unreachable\n");
|
|
|
|
}
|
2013-05-27 01:30:44 +04:00
|
|
|
}
|
|
|
|
|
2018-11-30 06:56:29 +03:00
|
|
|
/*
|
|
|
|
* thread-safe and called from non-Ruby thread
|
|
|
|
* returns FALSE on failure (ENOMEM), TRUE otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data)
|
|
|
|
{
|
|
|
|
struct rb_workqueue_job *wq_job = malloc(sizeof(*wq_job));
|
|
|
|
rb_vm_t *vm = GET_VM();
|
|
|
|
|
|
|
|
if (!wq_job) return FALSE;
|
|
|
|
wq_job->job.func = func;
|
|
|
|
wq_job->job.data = data;
|
|
|
|
|
|
|
|
rb_nativethread_lock_lock(&vm->workqueue_lock);
|
|
|
|
list_add_tail(&vm->workqueue, &wq_job->jnode);
|
|
|
|
rb_nativethread_lock_unlock(&vm->workqueue_lock);
|
|
|
|
|
|
|
|
RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC());
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-05-27 01:30:44 +04:00
|
|
|
void
|
|
|
|
rb_postponed_job_flush(rb_vm_t *vm)
|
|
|
|
{
|
2017-11-06 10:44:28 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
2018-11-18 07:34:52 +03:00
|
|
|
const rb_atomic_t block_mask = POSTPONED_JOB_INTERRUPT_MASK|TRAP_INTERRUPT_MASK;
|
|
|
|
volatile rb_atomic_t saved_mask = ec->interrupt_mask & block_mask;
|
2017-11-06 10:44:28 +03:00
|
|
|
VALUE volatile saved_errno = ec->errinfo;
|
2018-11-30 06:56:29 +03:00
|
|
|
struct list_head tmp;
|
|
|
|
|
|
|
|
list_head_init(&tmp);
|
|
|
|
|
|
|
|
rb_nativethread_lock_lock(&vm->workqueue_lock);
|
|
|
|
list_append_list(&tmp, &vm->workqueue);
|
|
|
|
rb_nativethread_lock_unlock(&vm->workqueue_lock);
|
2013-10-11 11:54:26 +04:00
|
|
|
|
2017-11-06 10:44:28 +03:00
|
|
|
ec->errinfo = Qnil;
|
2013-10-11 11:54:26 +04:00
|
|
|
/* mask POSTPONED_JOB dispatch */
|
2017-11-06 10:44:28 +03:00
|
|
|
ec->interrupt_mask |= block_mask;
|
2013-10-11 11:54:26 +04:00
|
|
|
{
|
2017-11-06 10:44:28 +03:00
|
|
|
EC_PUSH_TAG(ec);
|
2017-12-06 06:16:08 +03:00
|
|
|
if (EC_EXEC_TAG() == TAG_NONE) {
|
2018-10-23 06:47:48 +03:00
|
|
|
int index;
|
2018-11-30 06:56:29 +03:00
|
|
|
struct rb_workqueue_job *wq_job;
|
|
|
|
|
2018-10-23 06:47:48 +03:00
|
|
|
while ((index = vm->postponed_job_index) > 0) {
|
|
|
|
if (ATOMIC_CAS(vm->postponed_job_index, index, index-1) == index) {
|
|
|
|
rb_postponed_job_t *pjob = &vm->postponed_job_buffer[index-1];
|
|
|
|
(*pjob->func)(pjob->data);
|
|
|
|
}
|
2013-10-10 22:36:54 +04:00
|
|
|
}
|
2018-11-30 06:56:29 +03:00
|
|
|
while ((wq_job = list_pop(&tmp, struct rb_workqueue_job, jnode))) {
|
|
|
|
rb_postponed_job_t pjob = wq_job->job;
|
|
|
|
|
|
|
|
free(wq_job);
|
|
|
|
(pjob.func)(pjob.data);
|
|
|
|
}
|
2013-10-10 08:56:32 +04:00
|
|
|
}
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_POP_TAG();
|
2013-05-27 01:30:44 +04:00
|
|
|
}
|
2013-10-11 11:54:26 +04:00
|
|
|
/* restore POSTPONED_JOB mask */
|
2017-11-06 10:44:28 +03:00
|
|
|
ec->interrupt_mask &= ~(saved_mask ^ block_mask);
|
|
|
|
ec->errinfo = saved_errno;
|
2018-11-30 06:56:29 +03:00
|
|
|
|
|
|
|
/* don't leak memory if a job threw an exception */
|
|
|
|
if (!list_empty(&tmp)) {
|
|
|
|
rb_nativethread_lock_lock(&vm->workqueue_lock);
|
|
|
|
list_prepend_list(&vm->workqueue, &tmp);
|
|
|
|
rb_nativethread_lock_unlock(&vm->workqueue_lock);
|
|
|
|
|
|
|
|
RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC());
|
|
|
|
}
|
2013-05-27 01:30:44 +04:00
|
|
|
}
|