rb-trace 0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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