rb-trace 0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +23 -0
- data/NEWS +2 -0
- data/Rakefile +121 -0
- data/ext/extconf.rb +8 -0
- data/ext/thread_pthread.h +24 -0
- data/ext/trace.c +160 -0
- data/ext/vm_core_mini.h +133 -0
- data/lib/eventbuffer.rb +172 -0
- data/lib/trace.rb +3 -0
- data/lib/trace_mod.rb +207 -0
- data/lib/tracefilter.rb +228 -0
- data/test/unit/test-event2bitmask.rb +26 -0
- data/test/unit/test-trace-hook.rb +95 -0
- data/test/unit/test-trace_mod.rb +42 -0
- data/test/unit/test-tracefilter.rb +115 -0
- metadata +95 -0
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.org>
|
2
|
+
All rights reserved.
|
3
|
+
*
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions
|
6
|
+
are met:
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in the
|
11
|
+
documentation and/or other materials provided with the distribution.
|
12
|
+
*
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
15
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
16
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
17
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
18
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
19
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
20
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
21
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
22
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
23
|
+
SUCH DAMAGE.
|
data/Rakefile
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
# -*- Ruby -*-
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'rake/testtask'
|
6
|
+
|
7
|
+
SO_NAME = 'trace.so'
|
8
|
+
|
9
|
+
PACKAGE_VERSION = open('ext/trace.c') do |f|
|
10
|
+
f.grep(/^#define TRACE_VERSION/).first[/"(.+)"/,1]
|
11
|
+
end
|
12
|
+
|
13
|
+
EXT_FILES = FileList[%w(ext/*.c ext/*.h)]
|
14
|
+
LIB_FILES = FileList['lib/*.rb']
|
15
|
+
TEST_FILES = FileList['test/**/*.rb']
|
16
|
+
COMMON_FILES = FileList[%w(Rakefile NEWS LICENSE)]
|
17
|
+
ALL_FILES = COMMON_FILES + TEST_FILES + EXT_FILES + LIB_FILES
|
18
|
+
|
19
|
+
desc "Create a GNU-style ChangeLog via git2cl"
|
20
|
+
task :ChangeLog do
|
21
|
+
system("git log --pretty --numstat --summary | git2cl > ChangeLog")
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Test units - the smaller tests'
|
25
|
+
task :'test:unit' => [:ext] do |t|
|
26
|
+
Rake::TestTask.new(:'test:unit') do |t|
|
27
|
+
t.test_files = FileList['test/unit/**/*.rb']
|
28
|
+
# t.pattern = 'test/**/*test-*.rb' # instead of above
|
29
|
+
t.verbose = true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Create the core ruby-debug shared library extension"
|
34
|
+
task :ext do
|
35
|
+
Dir.chdir('ext') do
|
36
|
+
system("#{Gem.ruby} extconf.rb && make")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
desc 'Remove built files'
|
41
|
+
task :clean do
|
42
|
+
cd 'ext' do
|
43
|
+
if File.exist?('Makefile')
|
44
|
+
sh 'make clean'
|
45
|
+
rm 'Makefile'
|
46
|
+
end
|
47
|
+
derived_files = Dir.glob('.o') + Dir.glob('*.so')
|
48
|
+
rm derived_files unless derived_files.empty?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
desc 'Test everything - unit tests for now.'
|
53
|
+
task :test do
|
54
|
+
exceptions = ['test:unit'].collect do |task|
|
55
|
+
begin
|
56
|
+
Rake::Task[task].invoke
|
57
|
+
nil
|
58
|
+
rescue => e
|
59
|
+
e
|
60
|
+
end
|
61
|
+
end.compact
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "Test everything - same as test."
|
65
|
+
task :check => :test
|
66
|
+
task :default => [:test]
|
67
|
+
|
68
|
+
# Base GEM Specification
|
69
|
+
spec = Gem::Specification.new do |spec|
|
70
|
+
spec.name = "rb-trace"
|
71
|
+
|
72
|
+
spec.homepage = "http://github.com/rocky/rb-trace/tree/master"
|
73
|
+
spec.summary = "Trace hook extensions"
|
74
|
+
spec.description = <<-EOF
|
75
|
+
|
76
|
+
rb-trace adds a trace_hook object, translates hooks bitmasks to sets
|
77
|
+
and vice versa, and extends set_trace_func to ignore frames or
|
78
|
+
functions.
|
79
|
+
EOF
|
80
|
+
|
81
|
+
spec.version = PACKAGE_VERSION
|
82
|
+
spec.extensions = ['ext/extconf.rb']
|
83
|
+
|
84
|
+
spec.author = "R. Bernstein"
|
85
|
+
spec.email = "rockyb@rubyforge.org"
|
86
|
+
spec.platform = Gem::Platform::RUBY
|
87
|
+
spec.files = ALL_FILES.to_a
|
88
|
+
spec.add_dependency('rb-threadframe', '>= 0.32')
|
89
|
+
|
90
|
+
spec.required_ruby_version = '>= 1.9.2'
|
91
|
+
spec.date = Time.now
|
92
|
+
# spec.rubyforge_project = 'rocky-hacks'
|
93
|
+
|
94
|
+
# rdoc
|
95
|
+
spec.has_rdoc = true
|
96
|
+
# spec.extra_rdoc_files = ['README', 'threadframe.rd']
|
97
|
+
end
|
98
|
+
|
99
|
+
# Rake task to build the default package
|
100
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
101
|
+
pkg.need_tar = true
|
102
|
+
end
|
103
|
+
|
104
|
+
def install(spec, *opts)
|
105
|
+
args = ['gem', 'install', "pkg/#{spec.name}-#{spec.version}.gem"] + opts
|
106
|
+
system(*args)
|
107
|
+
end
|
108
|
+
|
109
|
+
desc 'Install locally'
|
110
|
+
task :install => :package do
|
111
|
+
Dir.chdir(File::dirname(__FILE__)) do
|
112
|
+
# ri and rdoc take lots of time
|
113
|
+
install(spec, '--no-ri', '--no-rdoc')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
task :install_full => :package do
|
118
|
+
Dir.chdir(File::dirname(__FILE__)) do
|
119
|
+
install(spec)
|
120
|
+
end
|
121
|
+
end
|
data/ext/extconf.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
/**********************************************************************
|
2
|
+
|
3
|
+
thread_pthread.h -
|
4
|
+
|
5
|
+
$Author$
|
6
|
+
|
7
|
+
Copyright (C) 2004-2007 Koichi Sasada
|
8
|
+
|
9
|
+
**********************************************************************/
|
10
|
+
|
11
|
+
#ifndef RUBY_THREAD_PTHREAD_H
|
12
|
+
#define RUBY_THREAD_PTHREAD_H
|
13
|
+
|
14
|
+
#include <pthread.h>
|
15
|
+
typedef pthread_t rb_thread_id_t;
|
16
|
+
typedef pthread_mutex_t rb_thread_lock_t;
|
17
|
+
typedef pthread_cond_t rb_thread_cond_t;
|
18
|
+
|
19
|
+
typedef struct native_thread_data_struct {
|
20
|
+
void *signal_thread_list;
|
21
|
+
pthread_cond_t sleep_cond;
|
22
|
+
} native_thread_data_t;
|
23
|
+
|
24
|
+
#endif /* RUBY_THREAD_PTHREAD_H */
|
data/ext/trace.c
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
#include "vm_core_mini.h" /* Pulls in ruby.h */
|
2
|
+
|
3
|
+
/* What release we got? */
|
4
|
+
#define TRACE_VERSION "0.2"
|
5
|
+
|
6
|
+
extern VALUE rb_cRubyVM; /* RubyVM class */
|
7
|
+
extern rb_vm_t *ruby_current_vm;
|
8
|
+
extern VALUE rb_obj_is_proc(VALUE proc);
|
9
|
+
|
10
|
+
VALUE rb_cTraceHook; /* TraceHook class */
|
11
|
+
VALUE rb_eTraceHookError; /* Exception raised by TraceHook class */
|
12
|
+
|
13
|
+
/* Extra information we need to save about a hook.
|
14
|
+
FIXME: For now we will work only with vm hooks. When this
|
15
|
+
extends to thread hooks, we will need to use this.
|
16
|
+
*/
|
17
|
+
typedef struct
|
18
|
+
{
|
19
|
+
rb_thread_t *th; /* If NULL, hook is in vm. Otherwise, this
|
20
|
+
is the thread the hook belongs to. */
|
21
|
+
rb_event_hook_t *hook;
|
22
|
+
} hook_info_t;
|
23
|
+
|
24
|
+
static int is_hook_member(rb_event_hook_t *check_hook, rb_event_hook_t *hook);
|
25
|
+
|
26
|
+
static void
|
27
|
+
check_hook_valid(rb_event_hook_t *check_hook)
|
28
|
+
{
|
29
|
+
/* FIXME: in the future use check_hook to find the hook head. */
|
30
|
+
rb_event_hook_t *hook_head = GET_VM()->event_hooks;
|
31
|
+
|
32
|
+
if (!is_hook_member(check_hook, hook_head))
|
33
|
+
rb_raise(rb_eTraceHookError, "hook not found");
|
34
|
+
}
|
35
|
+
|
36
|
+
/* Return an Array of vm event hooks found from hook. */
|
37
|
+
VALUE
|
38
|
+
get_trace_hooks(rb_event_hook_t *hook)
|
39
|
+
{
|
40
|
+
VALUE ary;
|
41
|
+
for (ary = rb_ary_new(); hook; hook = hook->next)
|
42
|
+
rb_ary_push(ary, Data_Wrap_Struct(rb_cTraceHook, NULL, NULL, hook));
|
43
|
+
return ary;
|
44
|
+
}
|
45
|
+
|
46
|
+
/* Return 1 if check_hook is found in the list of hooks pointed to by
|
47
|
+
'hook', or 0 if not found. */
|
48
|
+
static int
|
49
|
+
is_hook_member(rb_event_hook_t *check_hook, rb_event_hook_t *hook)
|
50
|
+
{
|
51
|
+
for (; hook; hook = hook->next) if (check_hook == hook) return 1;
|
52
|
+
return 0; /* Not found */
|
53
|
+
}
|
54
|
+
|
55
|
+
/* Return an Array of VM event hooks objects. */
|
56
|
+
VALUE
|
57
|
+
trace_hook_s_trace_hooks()
|
58
|
+
{
|
59
|
+
return get_trace_hooks(GET_VM()->event_hooks);
|
60
|
+
}
|
61
|
+
|
62
|
+
/*
|
63
|
+
Return the event mask value for a given hook. If no hook, then return nil.
|
64
|
+
*/
|
65
|
+
static VALUE
|
66
|
+
trace_hook_event_mask(VALUE klass)
|
67
|
+
{
|
68
|
+
rb_event_hook_t *hook;
|
69
|
+
Data_Get_Struct(klass, rb_event_hook_t, hook);
|
70
|
+
if (!hook) return Qnil;
|
71
|
+
check_hook_valid(hook);
|
72
|
+
return INT2FIX(hook->flag);
|
73
|
+
}
|
74
|
+
|
75
|
+
/* Set a new mask value for given hook and return the old mask
|
76
|
+
value. Can raise an error if there is no hook installed. */
|
77
|
+
static VALUE
|
78
|
+
trace_hook_event_mask_set(VALUE klass, VALUE maskval)
|
79
|
+
{
|
80
|
+
rb_event_hook_t *hook;
|
81
|
+
rb_event_flag_t flag;
|
82
|
+
Data_Get_Struct(klass, rb_event_hook_t, hook);
|
83
|
+
if (!hook)
|
84
|
+
rb_raise(rb_eTraceHookError, "No hook installed");
|
85
|
+
if (!FIXNUM_P(maskval)) {
|
86
|
+
rb_raise(rb_eTypeError, "integer argument expected");
|
87
|
+
}
|
88
|
+
check_hook_valid(hook);
|
89
|
+
flag = hook->flag;
|
90
|
+
hook->flag = FIX2INT(maskval);
|
91
|
+
return INT2FIX(flag);
|
92
|
+
}
|
93
|
+
|
94
|
+
/*
|
95
|
+
Return the event mask value for a given hook. If no hook, then return nil.
|
96
|
+
*/
|
97
|
+
static VALUE
|
98
|
+
trace_hook_proc(VALUE klass)
|
99
|
+
{
|
100
|
+
rb_event_hook_t *hook;
|
101
|
+
Data_Get_Struct(klass, rb_event_hook_t, hook);
|
102
|
+
if (!hook) return Qnil;
|
103
|
+
check_hook_valid(hook);
|
104
|
+
return hook->data;
|
105
|
+
}
|
106
|
+
|
107
|
+
/*
|
108
|
+
Return the event mask value for a given hook. If no hook, then return nil.
|
109
|
+
*/
|
110
|
+
static VALUE
|
111
|
+
trace_hook_proc_set(VALUE klass, VALUE trace_proc)
|
112
|
+
{
|
113
|
+
rb_event_hook_t *hook;
|
114
|
+
if (!rb_obj_is_proc(trace_proc)) {
|
115
|
+
rb_raise(rb_eTypeError, "trace_func needs to be Proc");
|
116
|
+
}
|
117
|
+
|
118
|
+
Data_Get_Struct(klass, rb_event_hook_t, hook);
|
119
|
+
if (!hook) return Qnil;
|
120
|
+
check_hook_valid(hook);
|
121
|
+
hook->data = trace_proc;
|
122
|
+
return trace_proc;
|
123
|
+
}
|
124
|
+
|
125
|
+
/*
|
126
|
+
Return true if hook is still valid or is nil), false otherwise.
|
127
|
+
*/
|
128
|
+
static VALUE
|
129
|
+
trace_hook_valid(VALUE klass)
|
130
|
+
{
|
131
|
+
rb_event_hook_t *hook;
|
132
|
+
Data_Get_Struct(klass, rb_event_hook_t, hook);
|
133
|
+
/* FIXME in the future we will need to extract whether this hook is
|
134
|
+
part of a thread or from the vm.
|
135
|
+
*/
|
136
|
+
return is_hook_member(hook, GET_VM()->event_hooks) ? Qtrue : Qfalse;
|
137
|
+
}
|
138
|
+
|
139
|
+
void
|
140
|
+
Init_trace(void)
|
141
|
+
{
|
142
|
+
rb_eTraceHookError = rb_define_class_under(rb_cRubyVM, "TraceHookError",
|
143
|
+
rb_eStandardError);
|
144
|
+
rb_cTraceHook = rb_define_class_under(rb_cRubyVM, "TraceHook",
|
145
|
+
rb_cObject);
|
146
|
+
|
147
|
+
rb_define_singleton_method(rb_cTraceHook, "trace_hooks",
|
148
|
+
trace_hook_s_trace_hooks, 0);
|
149
|
+
|
150
|
+
rb_define_method(rb_cTraceHook, "event_mask",
|
151
|
+
trace_hook_event_mask, 0);
|
152
|
+
rb_define_method(rb_cTraceHook, "event_mask=",
|
153
|
+
trace_hook_event_mask_set, 1);
|
154
|
+
rb_define_method(rb_cTraceHook, "proc",
|
155
|
+
trace_hook_proc, 0);
|
156
|
+
rb_define_method(rb_cTraceHook, "proc=",
|
157
|
+
trace_hook_proc_set, 1);
|
158
|
+
rb_define_method(rb_cTraceHook, "valid?",
|
159
|
+
trace_hook_valid, 0);
|
160
|
+
}
|
data/ext/vm_core_mini.h
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
/* Headers Exposing a little more of the 1.9 runtime and some
|
2
|
+
method prototypes for extensions to the Thread class.
|
3
|
+
*/
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <signal.h>
|
6
|
+
#include "thread_pthread.h"
|
7
|
+
|
8
|
+
/* From vm_core.h: */
|
9
|
+
|
10
|
+
#define GET_VM() ruby_current_vm
|
11
|
+
#define GET_THREAD() ruby_current_thread
|
12
|
+
|
13
|
+
#if 1
|
14
|
+
#define GetCoreDataFromValue(obj, type, ptr) do { \
|
15
|
+
ptr = (type*)DATA_PTR(obj); \
|
16
|
+
} while (0)
|
17
|
+
#else
|
18
|
+
#define GetCoreDataFromValue(obj, type, ptr) Data_Get_Struct(obj, type, ptr)
|
19
|
+
#endif
|
20
|
+
#if 1
|
21
|
+
#define GetCoreDataFromValue(obj, type, ptr) do { \
|
22
|
+
ptr = (type*)DATA_PTR(obj); \
|
23
|
+
} while (0)
|
24
|
+
#else
|
25
|
+
#define GetCoreDataFromValue(obj, type, ptr) Data_Get_Struct(obj, type, ptr)
|
26
|
+
#endif
|
27
|
+
|
28
|
+
/* Opaque types (for now at least) */
|
29
|
+
typedef struct rb_iseq_struct rb_iseq_t;
|
30
|
+
typedef struct rb_method_entry_struct rb_method_entry_t;
|
31
|
+
typedef struct rb_proc_struct rb_proc_t;
|
32
|
+
|
33
|
+
typedef struct {
|
34
|
+
VALUE *pc; /* cfp[0] */
|
35
|
+
VALUE *sp; /* cfp[1] */
|
36
|
+
VALUE *bp; /* cfp[2] */
|
37
|
+
rb_iseq_t *iseq; /* cfp[3] */
|
38
|
+
VALUE flag; /* cfp[4] */
|
39
|
+
VALUE self; /* cfp[5] / block[0] */
|
40
|
+
VALUE *lfp; /* cfp[6] / block[1] */
|
41
|
+
VALUE *dfp; /* cfp[7] / block[2] */
|
42
|
+
rb_iseq_t *block_iseq; /* cfp[8] / block[3] */
|
43
|
+
VALUE proc; /* cfp[9] / block[4] */
|
44
|
+
const rb_method_entry_t *me;/* cfp[10] */
|
45
|
+
} rb_control_frame_t;
|
46
|
+
|
47
|
+
enum ruby_special_exceptions {
|
48
|
+
ruby_error_reenter,
|
49
|
+
ruby_error_nomemory,
|
50
|
+
ruby_error_sysstack,
|
51
|
+
ruby_special_error_count
|
52
|
+
};
|
53
|
+
|
54
|
+
#define GetThreadPtr(obj, ptr) \
|
55
|
+
GetCoreDataFromValue(obj, rb_thread_t, ptr)
|
56
|
+
|
57
|
+
#define GetProcPtr(obj, ptr) \
|
58
|
+
GetCoreDataFromValue(obj, rb_proc_t, ptr)
|
59
|
+
|
60
|
+
#ifndef NSIG
|
61
|
+
# define NSIG (_SIGMAX + 1) /* For QNX */
|
62
|
+
#endif
|
63
|
+
|
64
|
+
#define RUBY_NSIG NSIG
|
65
|
+
typedef struct rb_vm_struct {
|
66
|
+
VALUE self;
|
67
|
+
|
68
|
+
rb_thread_lock_t global_vm_lock;
|
69
|
+
|
70
|
+
struct rb_thread_struct *main_thread;
|
71
|
+
struct rb_thread_struct *running_thread;
|
72
|
+
|
73
|
+
st_table *living_threads;
|
74
|
+
VALUE thgroup_default;
|
75
|
+
|
76
|
+
int running;
|
77
|
+
int thread_abort_on_exception;
|
78
|
+
unsigned long trace_flag;
|
79
|
+
volatile int sleeper;
|
80
|
+
|
81
|
+
/* object management */
|
82
|
+
VALUE mark_object_ary;
|
83
|
+
|
84
|
+
VALUE special_exceptions[ruby_special_error_count];
|
85
|
+
|
86
|
+
/* load */
|
87
|
+
VALUE top_self;
|
88
|
+
VALUE load_path;
|
89
|
+
VALUE loaded_features;
|
90
|
+
struct st_table *loading_table;
|
91
|
+
|
92
|
+
/* signal */
|
93
|
+
struct {
|
94
|
+
VALUE cmd;
|
95
|
+
int safe;
|
96
|
+
} trap_list[RUBY_NSIG];
|
97
|
+
|
98
|
+
/* hook */
|
99
|
+
rb_event_hook_t *event_hooks;
|
100
|
+
|
101
|
+
int src_encoding_index;
|
102
|
+
|
103
|
+
VALUE verbose, debug, progname;
|
104
|
+
VALUE coverages;
|
105
|
+
|
106
|
+
#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
|
107
|
+
struct rb_objspace *objspace;
|
108
|
+
#endif
|
109
|
+
} rb_vm_t;
|
110
|
+
|
111
|
+
typedef struct rb_thread_struct
|
112
|
+
{
|
113
|
+
VALUE self;
|
114
|
+
rb_vm_t *vm;
|
115
|
+
|
116
|
+
/* execution information */
|
117
|
+
VALUE *stack; /* must free, must mark. rb: seems to be nil. */
|
118
|
+
unsigned long stack_size; /* Number of stack (or rb_control_frame_t) entries */
|
119
|
+
rb_control_frame_t *cfp;
|
120
|
+
|
121
|
+
int safe_level;
|
122
|
+
int raised_flag;
|
123
|
+
VALUE last_status; /* $? */
|
124
|
+
|
125
|
+
/* passing state */
|
126
|
+
int state;
|
127
|
+
|
128
|
+
/* Lot's of other stuff ... */
|
129
|
+
} rb_thread_t;
|
130
|
+
|
131
|
+
extern rb_thread_t *ruby_current_thread;
|
132
|
+
|
133
|
+
|
data/lib/eventbuffer.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
module Trace
|
2
|
+
|
3
|
+
class EventBuffer
|
4
|
+
EventStruct = Struct.new(:event, :arg, :type, :thread, :method,
|
5
|
+
:source_container, :source_location,
|
6
|
+
:iseq, :pc_offset) unless defined?(EventStruct)
|
7
|
+
attr_reader :buf
|
8
|
+
attr_accessor :marks # User position mark into buffer. If buffer is limited,
|
9
|
+
attr_reader :maxsize # Maximum size of buffer or nil if unlimited.
|
10
|
+
attr_reader :size # size of buffer
|
11
|
+
# then marks will drop out as they disappear from the buffer
|
12
|
+
def initialize(maxsize=nil)
|
13
|
+
@maxsize = maxsize
|
14
|
+
reset
|
15
|
+
end
|
16
|
+
|
17
|
+
def reset
|
18
|
+
@buf = []
|
19
|
+
@marks = []
|
20
|
+
@pos = -1
|
21
|
+
@size = 0
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add a new event dropping off old events if that was declared
|
25
|
+
# marks are also dropped if buffer has a limit.
|
26
|
+
def append(event, frame, arg)
|
27
|
+
if 'c-return' == event
|
28
|
+
arg = frame.sp(2)
|
29
|
+
elsif 'return' == event
|
30
|
+
arg = frame.sp(1)
|
31
|
+
end
|
32
|
+
|
33
|
+
iseq = frame.iseq
|
34
|
+
item = EventStruct.new(event, arg, frame.type, frame.thread, frame.method,
|
35
|
+
frame.source_container, frame.source_location,
|
36
|
+
iseq, iseq ? frame.pc_offset : nil)
|
37
|
+
@pos = self.succ_pos
|
38
|
+
@marks.shift if @marks[0] == @pos
|
39
|
+
@buf[@pos] = item
|
40
|
+
@size += 1 unless @maxsize && @size == @maxsize
|
41
|
+
end
|
42
|
+
|
43
|
+
# Add mark for the current event buffer position.
|
44
|
+
def add_mark
|
45
|
+
@marks << @pos
|
46
|
+
end
|
47
|
+
|
48
|
+
# Like add mark, but do only if the last marked position has
|
49
|
+
# changed
|
50
|
+
def add_mark_nodup
|
51
|
+
@marks << @pos unless @marks[-1] == @pos
|
52
|
+
end
|
53
|
+
|
54
|
+
def each(from=nil, to=nil)
|
55
|
+
from = self.succ_pos unless from
|
56
|
+
to = @pos unless to
|
57
|
+
if from <= to
|
58
|
+
from.upto(to).each do |pos|
|
59
|
+
yield @buf[pos]
|
60
|
+
end
|
61
|
+
else
|
62
|
+
from.upto(@size-1).each do |pos|
|
63
|
+
yield @buf[pos]
|
64
|
+
end
|
65
|
+
0.upto(@pos).each do |pos|
|
66
|
+
yield @buf[pos]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def each_with_index(from=nil, to=nil)
|
72
|
+
from = succ_pos unless from
|
73
|
+
to = @pos unless to
|
74
|
+
if from <= to
|
75
|
+
from.upto(to).each do |pos|
|
76
|
+
yield [@buf[pos], pos]
|
77
|
+
end
|
78
|
+
else
|
79
|
+
from.upto(@size-1).each do |pos|
|
80
|
+
yield [@buf[pos], pos]
|
81
|
+
end
|
82
|
+
0.upto(@pos).each do |pos|
|
83
|
+
yield [@buf[pos], pos]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def format_entry(item, long_format=true)
|
89
|
+
# require 'rbdbgr'; Debugger.debug
|
90
|
+
container =
|
91
|
+
if item.source_container[0] == 'file'
|
92
|
+
item.source_container[1].inspect
|
93
|
+
else
|
94
|
+
item.source_container.inspect
|
95
|
+
end
|
96
|
+
|
97
|
+
location =
|
98
|
+
if 1 == item.source_location.size
|
99
|
+
item.source_location[0].inspect
|
100
|
+
else
|
101
|
+
item.source_location.inspect
|
102
|
+
end
|
103
|
+
|
104
|
+
mess = "#{item.event} #{item.type} #{item.method} " +
|
105
|
+
"#{container} #{location}"
|
106
|
+
if long_format && item.iseq
|
107
|
+
mess += "\n\t" + "VM offset #{item.pc_offset} of #{item.iseq.name}"
|
108
|
+
end
|
109
|
+
mess
|
110
|
+
end
|
111
|
+
|
112
|
+
# Return the next event buffer position taking into account
|
113
|
+
# that we may have a fixed-sized buffer ring.
|
114
|
+
def succ_pos(inc=1)
|
115
|
+
pos = @pos + inc
|
116
|
+
@maxsize ? pos % @maxsize : pos
|
117
|
+
end
|
118
|
+
|
119
|
+
# Return the next event buffer position taking into account
|
120
|
+
# that we may have a fixed-sized buffer ring.
|
121
|
+
def pred_pos(dec=1)
|
122
|
+
pos = @pos - dec
|
123
|
+
@maxsize ? pos % @maxsize : pos
|
124
|
+
end
|
125
|
+
|
126
|
+
# Return the adjusted zeroth position in @buf.
|
127
|
+
def zero_pos
|
128
|
+
if !@maxsize || @buf.size < @maxsize
|
129
|
+
0
|
130
|
+
else
|
131
|
+
self.succ_pos
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end # EventBuffer
|
136
|
+
end # Trace
|
137
|
+
|
138
|
+
if __FILE__ == $0
|
139
|
+
def event_processor(event, frame, arg=nil)
|
140
|
+
begin
|
141
|
+
@eventbuf.append(event, frame, arg)
|
142
|
+
rescue
|
143
|
+
p $!
|
144
|
+
end
|
145
|
+
end
|
146
|
+
def dump_all
|
147
|
+
puts '-' * 40
|
148
|
+
@eventbuf.each do |e|
|
149
|
+
puts @eventbuf.format_entry(e) if e
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
require_relative 'trace'
|
154
|
+
@eventbuf = Trace::EventBuffer.new(5)
|
155
|
+
p @eventbuf.zero_pos
|
156
|
+
dump_all
|
157
|
+
|
158
|
+
trace_filter = Trace::Filter.new
|
159
|
+
trace_func = method(:event_processor).to_proc
|
160
|
+
trace_filter << trace_func
|
161
|
+
trace_filter.set_trace_func(trace_func)
|
162
|
+
z=5
|
163
|
+
z.times do |i|
|
164
|
+
x = i
|
165
|
+
y = x+2
|
166
|
+
end
|
167
|
+
trace_filter.set_trace_func(nil)
|
168
|
+
p @eventbuf.buf[@eventbuf.zero_pos]
|
169
|
+
dump_all
|
170
|
+
@eventbuf.reset
|
171
|
+
dump_all
|
172
|
+
end
|
data/lib/trace.rb
ADDED