rotoscope 0.3.0.pre.7 → 0.3.0.pre.8
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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/README.md +45 -89
- data/Rakefile +6 -1
- data/bin/fmt +1 -1
- data/dev.yml +1 -1
- data/ext/rotoscope/method_desc.c +11 -0
- data/ext/rotoscope/method_desc.h +16 -0
- data/ext/rotoscope/rotoscope.c +133 -199
- data/ext/rotoscope/rotoscope.h +4 -37
- data/ext/rotoscope/stack.c +5 -41
- data/ext/rotoscope/stack.h +3 -9
- data/lib/rotoscope.rb +5 -62
- data/lib/rotoscope/call_logger.rb +136 -0
- data/lib/rotoscope/version.rb +1 -1
- data/rotoscope.gemspec +1 -1
- metadata +6 -5
- data/ext/rotoscope/tracepoint.c +0 -8
- data/ext/rotoscope/tracepoint.h +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a13014ea4da609b9a22d6abf8bbd57d6f2d33ffe
|
4
|
+
data.tar.gz: 1d3c71e1673bfc4cc4adfec7e5041e66f0104999
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2acdaacb7ee98c49c437ffd73e6444dbbf0c0b8dbeedad1edef839c3dde20ff9344a331b1747be6a4a306855dd818a094768dd16d091a41b70a1ff39636cd0d
|
7
|
+
data.tar.gz: f2e35df44720ea2c7d53c5c8c548b99867772d02fa5c6cd56d2b863d982c4635840d2c74b328c05053718aa264746d0f450f2e4168aa6a30bca4b377234160e7
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.4.3
|
data/README.md
CHANGED
@@ -28,7 +28,7 @@ end
|
|
28
28
|
log_file = File.expand_path('dog_trace.log')
|
29
29
|
puts "Writing to #{log_file}..."
|
30
30
|
|
31
|
-
Rotoscope.trace(log_file) do
|
31
|
+
Rotoscope::CallLogger.trace(log_file) do
|
32
32
|
dog1 = Dog.new
|
33
33
|
dog1.bark
|
34
34
|
end
|
@@ -38,131 +38,98 @@ The resulting method calls are saved in the specified `dest` in the order they w
|
|
38
38
|
|
39
39
|
Sample output:
|
40
40
|
|
41
|
-
```
|
42
|
-
event,entity,method_name,method_level,filepath,lineno
|
43
|
-
call,"Dog","new",class,"example/dog.rb",19
|
44
|
-
call,"Dog","initialize",instance,"example/dog.rb",19
|
45
|
-
return,"Dog","initialize",instance,"example/dog.rb",19
|
46
|
-
return,"Dog","new",class,"example/dog.rb",19
|
47
|
-
call,"Dog","bark",instance,"example/dog.rb",4
|
48
|
-
call,"Noisemaker","speak",class,"example/dog.rb",10
|
49
|
-
call,"Noisemaker","puts",class,"example/dog.rb",11
|
50
|
-
call,"IO","puts",instance,"example/dog.rb",11
|
51
|
-
call,"IO","write",instance,"example/dog.rb",11
|
52
|
-
return,"IO","write",instance,"example/dog.rb",11
|
53
|
-
call,"IO","write",instance,"example/dog.rb",11
|
54
|
-
return,"IO","write",instance,"example/dog.rb",11
|
55
|
-
return,"IO","puts",instance,"example/dog.rb",11
|
56
|
-
return,"Noisemaker","puts",class,"example/dog.rb",11
|
57
|
-
return,"Noisemaker","speak",class,"example/dog.rb",12
|
58
|
-
return,"Dog","bark",instance,"example/dog.rb",6
|
59
|
-
```
|
60
|
-
|
61
|
-
If you're interested solely in the flattened caller/callee list, you can pass the `flatten` option to retrieve that instead. This step will also remove all duplicate lines, which can produce significantly smaller output on large codebases.
|
62
|
-
|
63
|
-
```ruby
|
64
|
-
# ... same code as above
|
65
|
-
|
66
|
-
Rotoscope.trace(log_file, flatten: true) do
|
67
|
-
dog1 = Dog.new
|
68
|
-
dog1.bark
|
69
|
-
end
|
70
|
-
```
|
71
|
-
|
72
|
-
Sample output:
|
73
|
-
|
74
41
|
```
|
75
42
|
entity,method_name,method_level,filepath,lineno,caller_entity,caller_method_name,caller_method_level
|
76
|
-
Dog,new,class,example/
|
77
|
-
Dog,initialize,instance,example/
|
78
|
-
Dog,bark,instance,example/
|
79
|
-
Noisemaker,speak,class,example/
|
80
|
-
Noisemaker,puts,class,example/
|
81
|
-
IO,puts,instance,example/
|
82
|
-
IO,write,instance,example/
|
43
|
+
Dog,new,class,example/dog.rb,19,<ROOT>,<UNKNOWN>,<UNKNOWN>
|
44
|
+
Dog,initialize,instance,example/dog.rb,19,Dog,new,class
|
45
|
+
Dog,bark,instance,example/dog.rb,20,<ROOT>,<UNKNOWN>,<UNKNOWN>
|
46
|
+
Noisemaker,speak,class,example/dog.rb,5,Dog,bark,instance
|
47
|
+
Noisemaker,puts,class,example/dog.rb,11,Noisemaker,speak,class
|
48
|
+
IO,puts,instance,example/dog.rb,11,Noisemaker,puts,class
|
49
|
+
IO,write,instance,example/dog.rb,11,IO,puts,instance
|
50
|
+
IO,write,instance,example/dog.rb,11,IO,puts,instance
|
83
51
|
```
|
84
52
|
|
85
53
|
## API
|
86
54
|
|
87
55
|
- [Public Class Methods](#public-class-methods)
|
88
|
-
- [`trace`](#
|
89
|
-
- [`new`](#
|
56
|
+
- [`trace`](#rotoscopecallloggertracedest-blacklist-)
|
57
|
+
- [`new`](#rotoscopecallloggernewdest-blacklist-)
|
90
58
|
- [Public Instance Methods](#public-instance-methods)
|
91
|
-
- [`trace`](#
|
92
|
-
- [`start_trace`](#
|
93
|
-
- [`stop_trace`](#
|
94
|
-
- [`mark`](#
|
95
|
-
- [`close`](#
|
96
|
-
- [`state`](#
|
97
|
-
- [`closed?`](#
|
98
|
-
- [`log_path`](#rotoscopelog_path)
|
59
|
+
- [`trace`](#rotoscopecallloggertraceblock)
|
60
|
+
- [`start_trace`](#rotoscopecallloggerstart_trace)
|
61
|
+
- [`stop_trace`](#rotoscopecallloggerstop_trace)
|
62
|
+
- [`mark`](#rotoscopecallloggermarkstr--)
|
63
|
+
- [`close`](#rotoscopecallloggerclose)
|
64
|
+
- [`state`](#rotoscopecallloggerstate)
|
65
|
+
- [`closed?`](#rotoscopecallloggerclosed)
|
99
66
|
|
100
67
|
---
|
101
68
|
|
102
69
|
### Public Class Methods
|
103
70
|
|
104
|
-
#### `Rotoscope::trace(dest, blacklist: []
|
71
|
+
#### `Rotoscope::CallLogger::trace(dest, blacklist: [])`
|
105
72
|
|
106
|
-
Writes all calls
|
73
|
+
Writes all calls of methods to `dest`, except for those whose filepath contains any entry in `blacklist`. `dest` is either a filename or an `IO`. Methods invoked at the top of the trace will have a caller entity of `<ROOT>` and a caller method name of `<UNKNOWN>`.
|
107
74
|
|
108
75
|
```ruby
|
109
|
-
Rotoscope.trace(dest) { |rs| ... }
|
76
|
+
Rotoscope::CallLogger.trace(dest) { |rs| ... }
|
110
77
|
# or...
|
111
|
-
Rotoscope.trace(dest, blacklist: ["/.gem/"]
|
78
|
+
Rotoscope::CallLogger.trace(dest, blacklist: ["/.gem/"]) { |rs| ... }
|
112
79
|
```
|
113
80
|
|
114
|
-
#### `Rotoscope::new(dest, blacklist: []
|
81
|
+
#### `Rotoscope::CallLogger::new(dest, blacklist: [])`
|
115
82
|
|
116
|
-
Same interface as `Rotoscope::trace`, but returns a `Rotoscope` instance, allowing fine-grain control via `Rotoscope#start_trace` and `Rotoscope#stop_trace`.
|
83
|
+
Same interface as `Rotoscope::CallLogger::trace`, but returns a `Rotoscope::CallLogger` instance, allowing fine-grain control via `Rotoscope::CallLogger#start_trace` and `Rotoscope::CallLogger#stop_trace`.
|
117
84
|
```ruby
|
118
|
-
rs = Rotoscope.new(dest)
|
85
|
+
rs = Rotoscope::CallLogger.new(dest)
|
119
86
|
# or...
|
120
|
-
rs = Rotoscope.new(dest, blacklist: ["/.gem/"]
|
87
|
+
rs = Rotoscope::CallLogger.new(dest, blacklist: ["/.gem/"])
|
121
88
|
```
|
122
89
|
|
123
90
|
---
|
124
91
|
|
125
92
|
### Public Instance Methods
|
126
93
|
|
127
|
-
#### `Rotoscope#trace(&block)`
|
94
|
+
#### `Rotoscope::CallLogger#trace(&block)`
|
128
95
|
|
129
|
-
Similar to `Rotoscope::trace`, but does not need to create a file handle on invocation.
|
96
|
+
Similar to `Rotoscope::CallLogger::trace`, but does not need to create a file handle on invocation.
|
130
97
|
|
131
98
|
```ruby
|
132
|
-
rs = Rotoscope.new(dest)
|
99
|
+
rs = Rotoscope::CallLogger.new(dest)
|
133
100
|
rs.trace do |rotoscope|
|
134
101
|
# code to trace...
|
135
102
|
end
|
136
103
|
```
|
137
104
|
|
138
|
-
#### `Rotoscope#start_trace`
|
105
|
+
#### `Rotoscope::CallLogger#start_trace`
|
139
106
|
|
140
|
-
Begins writing method calls
|
107
|
+
Begins writing method calls to the `dest` specified in the initializer.
|
141
108
|
|
142
109
|
```ruby
|
143
|
-
rs = Rotoscope.new(dest)
|
110
|
+
rs = Rotoscope::CallLogger.new(dest)
|
144
111
|
rs.start_trace
|
145
112
|
# code to trace...
|
146
113
|
rs.stop_trace
|
147
114
|
```
|
148
115
|
|
149
|
-
#### `Rotoscope#stop_trace`
|
116
|
+
#### `Rotoscope::CallLogger#stop_trace`
|
150
117
|
|
151
|
-
Stops writing method invocations to the `dest`. Subsequent calls to `Rotoscope#start_trace` may be invoked to resume tracing.
|
118
|
+
Stops writing method invocations to the `dest`. Subsequent calls to `Rotoscope::CallLogger#start_trace` may be invoked to resume tracing.
|
152
119
|
|
153
120
|
```ruby
|
154
|
-
rs = Rotoscope.new(dest)
|
121
|
+
rs = Rotoscope::CallLogger.new(dest)
|
155
122
|
rs.start_trace
|
156
123
|
# code to trace...
|
157
124
|
rs.stop_trace
|
158
125
|
```
|
159
126
|
|
160
|
-
#### `Rotoscope#mark(str = "")`
|
127
|
+
#### `Rotoscope::CallLogger#mark(str = "")`
|
161
128
|
|
162
129
|
Inserts a marker '--- ' to divide output. Useful for segmenting multiple blocks of code that are being profiled. If `str` is provided, the line will be prefixed by '--- ', followed by the string passed.
|
163
130
|
|
164
131
|
```ruby
|
165
|
-
rs = Rotoscope.new(dest)
|
132
|
+
rs = Rotoscope::CallLogger.new(dest)
|
166
133
|
rs.start_trace
|
167
134
|
# code to trace...
|
168
135
|
rs.mark('Something goes wrong here') # produces `--- Something goes wrong here` in the output
|
@@ -170,22 +137,22 @@ rs.mark('Something goes wrong here') # produces `--- Something goes wrong here`
|
|
170
137
|
rs.stop_trace
|
171
138
|
```
|
172
139
|
|
173
|
-
#### `Rotoscope#close`
|
140
|
+
#### `Rotoscope::CallLogger#close`
|
174
141
|
|
175
|
-
Flushes the buffer and closes the file handle. Once this is invoked, no more writes can be performed on the `Rotoscope` object. Sets `state` to `:closed`.
|
142
|
+
Flushes the buffer and closes the file handle. Once this is invoked, no more writes can be performed on the `Rotoscope::CallLogger` object. Sets `state` to `:closed`.
|
176
143
|
|
177
144
|
```ruby
|
178
|
-
rs = Rotoscope.new(dest)
|
145
|
+
rs = Rotoscope::CallLogger.new(dest)
|
179
146
|
rs.trace { |rotoscope| ... }
|
180
147
|
rs.close
|
181
148
|
```
|
182
149
|
|
183
|
-
#### `Rotoscope#state`
|
150
|
+
#### `Rotoscope::CallLogger#state`
|
184
151
|
|
185
|
-
Returns the current state of the Rotoscope object. Valid values are `:open`, `:tracing` and `:closed`.
|
152
|
+
Returns the current state of the Rotoscope::CallLogger object. Valid values are `:open`, `:tracing` and `:closed`.
|
186
153
|
|
187
154
|
```ruby
|
188
|
-
rs = Rotoscope.new(dest)
|
155
|
+
rs = Rotoscope::CallLogger.new(dest)
|
189
156
|
rs.state # :open
|
190
157
|
rs.trace do
|
191
158
|
rs.state # :tracing
|
@@ -194,24 +161,13 @@ rs.close
|
|
194
161
|
rs.state # :closed
|
195
162
|
```
|
196
163
|
|
197
|
-
#### `Rotoscope#closed?`
|
164
|
+
#### `Rotoscope::CallLogger#closed?`
|
198
165
|
|
199
166
|
Shorthand to check if the `state` is set to `:closed`.
|
200
167
|
|
201
168
|
```ruby
|
202
|
-
rs = Rotoscope.new(dest)
|
169
|
+
rs = Rotoscope::CallLogger.new(dest)
|
203
170
|
rs.closed? # false
|
204
171
|
rs.close
|
205
172
|
rs.closed? # true
|
206
173
|
```
|
207
|
-
|
208
|
-
|
209
|
-
#### `Rotoscope#log_path`
|
210
|
-
|
211
|
-
Returns the output filepath set in the Rotoscope constructor.
|
212
|
-
|
213
|
-
```ruby
|
214
|
-
dest = '/foo/bar/rotoscope.csv'
|
215
|
-
rs = Rotoscope.new(dest)
|
216
|
-
rs.log_path # "/foo/bar/rotoscope.csv"
|
217
|
-
```
|
data/Rakefile
CHANGED
data/bin/fmt
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#! /usr/bin/env bash
|
2
2
|
|
3
3
|
echo "Formatting C code..."
|
4
|
-
find "$PWD" -iname
|
4
|
+
find "$PWD/ext" -iname '*.c' -o -iname '*.h' | xargs clang-format -i -style=file
|
5
5
|
|
6
6
|
echo "Formatting Ruby code..."
|
7
7
|
bundle exec rubocop -a
|
data/dev.yml
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
#include "method_desc.h"
|
2
|
+
|
3
|
+
VALUE rs_method_class(rs_method_desc_t *method) {
|
4
|
+
return method->singleton_p ? method->receiver
|
5
|
+
: rb_obj_class(method->receiver);
|
6
|
+
}
|
7
|
+
|
8
|
+
void rs_method_desc_mark(rs_method_desc_t *method) {
|
9
|
+
rb_gc_mark(method->receiver);
|
10
|
+
rb_gc_mark(method->id);
|
11
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#ifndef _INC_ROTOSCOPE_METHOD_DESC_H_
|
2
|
+
#define _INC_ROTOSCOPE_METHOD_DESC_H_
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
#include "stdbool.h"
|
6
|
+
|
7
|
+
typedef struct rs_method_desc_t {
|
8
|
+
VALUE receiver;
|
9
|
+
VALUE id;
|
10
|
+
bool singleton_p;
|
11
|
+
} rs_method_desc_t;
|
12
|
+
|
13
|
+
VALUE rs_method_class(rs_method_desc_t *method);
|
14
|
+
void rs_method_desc_mark(rs_method_desc_t *method);
|
15
|
+
|
16
|
+
#endif
|
data/ext/rotoscope/rotoscope.c
CHANGED
@@ -9,39 +9,17 @@
|
|
9
9
|
#include <sys/file.h>
|
10
10
|
|
11
11
|
#include "callsite.h"
|
12
|
+
#include "method_desc.h"
|
12
13
|
#include "rotoscope.h"
|
13
14
|
#include "stack.h"
|
14
|
-
#include "tracepoint.h"
|
15
15
|
|
16
16
|
VALUE cRotoscope, cTracePoint;
|
17
|
-
ID id_initialize,
|
18
|
-
VALUE str_quote, str_escaped_quote, str_header;
|
17
|
+
ID id_initialize, id_call;
|
19
18
|
|
20
19
|
static unsigned long gettid() {
|
21
20
|
return NUM2ULONG(rb_obj_id(rb_thread_current()));
|
22
21
|
}
|
23
22
|
|
24
|
-
static const char *evflag2name(rb_event_flag_t evflag) {
|
25
|
-
switch (evflag) {
|
26
|
-
case RUBY_EVENT_CALL:
|
27
|
-
case RUBY_EVENT_C_CALL:
|
28
|
-
return "call";
|
29
|
-
case RUBY_EVENT_RETURN:
|
30
|
-
case RUBY_EVENT_C_RETURN:
|
31
|
-
return "return";
|
32
|
-
default:
|
33
|
-
return "unknown";
|
34
|
-
}
|
35
|
-
}
|
36
|
-
|
37
|
-
static bool rejected_path(VALUE path, Rotoscope *config) {
|
38
|
-
for (unsigned long i = 0; i < config->blacklist_size; i++) {
|
39
|
-
if (strstr(StringValueCStr(path), config->blacklist[i])) return true;
|
40
|
-
}
|
41
|
-
|
42
|
-
return false;
|
43
|
-
}
|
44
|
-
|
45
23
|
static VALUE class_path(VALUE klass) {
|
46
24
|
VALUE cached_path = rb_class_path_cached(klass);
|
47
25
|
if (!NIL_P(cached_path)) {
|
@@ -75,45 +53,18 @@ static rs_callsite_t tracearg_path(rb_trace_arg_t *trace_arg) {
|
|
75
53
|
}
|
76
54
|
}
|
77
55
|
|
78
|
-
static
|
79
|
-
VALUE
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
SYM2ID(rb_tracearg_method_id(trace_arg)) != id_initialize) {
|
85
|
-
method_level = CLASS_METHOD;
|
86
|
-
klass = self;
|
87
|
-
} else {
|
88
|
-
method_level = INSTANCE_METHOD;
|
89
|
-
klass = rb_obj_class(self);
|
90
|
-
}
|
56
|
+
static rs_method_desc_t called_method_desc(rb_trace_arg_t *trace_arg) {
|
57
|
+
VALUE receiver = rb_tracearg_self(trace_arg);
|
58
|
+
VALUE method_id = rb_tracearg_method_id(trace_arg);
|
59
|
+
bool singleton_p =
|
60
|
+
(RB_TYPE_P(receiver, T_CLASS) || RB_TYPE_P(receiver, T_MODULE)) &&
|
61
|
+
SYM2ID(method_id) != id_initialize;
|
91
62
|
|
92
|
-
return (
|
93
|
-
.
|
63
|
+
return (rs_method_desc_t){
|
64
|
+
.receiver = receiver, .id = method_id, .singleton_p = singleton_p,
|
94
65
|
};
|
95
66
|
}
|
96
67
|
|
97
|
-
static VALUE tracearg_method_name(rb_trace_arg_t *trace_arg) {
|
98
|
-
return rb_sym2str(rb_tracearg_method_id(trace_arg));
|
99
|
-
}
|
100
|
-
|
101
|
-
static rs_tracepoint_t extract_full_tracevals(rb_trace_arg_t *trace_arg,
|
102
|
-
const rs_callsite_t *callsite) {
|
103
|
-
rs_class_desc_t method_owner = tracearg_class(trace_arg);
|
104
|
-
rb_event_flag_t event_flag = rb_tracearg_event_flag(trace_arg);
|
105
|
-
|
106
|
-
VALUE method_name = tracearg_method_name(trace_arg);
|
107
|
-
VALUE filepath = callsite->filepath;
|
108
|
-
|
109
|
-
return (rs_tracepoint_t){.event = evflag2name(event_flag),
|
110
|
-
.entity = method_owner.name,
|
111
|
-
.filepath = filepath,
|
112
|
-
.method_name = method_name,
|
113
|
-
.method_level = method_owner.method_level,
|
114
|
-
.lineno = callsite->lineno};
|
115
|
-
}
|
116
|
-
|
117
68
|
static bool in_fork(Rotoscope *config) { return config->pid != getpid(); }
|
118
69
|
|
119
70
|
// The GC sweep step will turn objects with finalizers (e.g. rs_dealloc)
|
@@ -127,43 +78,8 @@ bool rotoscope_marked_for_garbage_collection(Rotoscope *config) {
|
|
127
78
|
return RB_BUILTIN_TYPE(config->self) == RUBY_T_ZOMBIE;
|
128
79
|
}
|
129
80
|
|
130
|
-
VALUE escape_csv_string(VALUE string) {
|
131
|
-
if (!memchr(RSTRING_PTR(string), '"', RSTRING_LEN(string))) {
|
132
|
-
return string;
|
133
|
-
}
|
134
|
-
return rb_funcall(string, id_gsub, 2, str_quote, str_escaped_quote);
|
135
|
-
}
|
136
|
-
|
137
|
-
static void log_trace_event_with_caller(VALUE output_buffer, VALUE io,
|
138
|
-
rs_stack_frame_t *stack_frame,
|
139
|
-
rs_stack_frame_t *caller_frame) {
|
140
|
-
VALUE escaped_method_name = escape_csv_string(stack_frame->tp.method_name);
|
141
|
-
VALUE escaped_caller_method_name =
|
142
|
-
escape_csv_string(caller_frame->tp.method_name);
|
143
|
-
|
144
|
-
while (true) {
|
145
|
-
rb_str_modify(output_buffer);
|
146
|
-
long out_len = snprintf(
|
147
|
-
RSTRING_PTR(output_buffer), rb_str_capacity(output_buffer),
|
148
|
-
RS_CSV_FORMAT "\n",
|
149
|
-
RS_CSV_VALUES(&stack_frame->tp, &caller_frame->tp, escaped_method_name,
|
150
|
-
escaped_caller_method_name));
|
151
|
-
|
152
|
-
if (out_len < RSTRING_LEN(output_buffer)) {
|
153
|
-
rb_str_set_len(output_buffer, out_len);
|
154
|
-
break;
|
155
|
-
}
|
156
|
-
rb_str_resize(output_buffer, out_len + 1);
|
157
|
-
}
|
158
|
-
|
159
|
-
RB_GC_GUARD(escaped_method_name);
|
160
|
-
RB_GC_GUARD(escaped_caller_method_name);
|
161
|
-
|
162
|
-
rb_io_write(io, output_buffer);
|
163
|
-
}
|
164
|
-
|
165
81
|
static void stop_tracing_on_cleanup(Rotoscope *config) {
|
166
|
-
if (config->
|
82
|
+
if (config->tracing) {
|
167
83
|
// During process cleanup, event hooks are removed and tracepoint may have
|
168
84
|
// already have been GCed, so we need a sanity check before disabling the
|
169
85
|
// tracepoint.
|
@@ -171,7 +87,7 @@ static void stop_tracing_on_cleanup(Rotoscope *config) {
|
|
171
87
|
CLASS_OF(config->tracepoint) == cTracePoint) {
|
172
88
|
rb_tracepoint_disable(config->tracepoint);
|
173
89
|
}
|
174
|
-
config->
|
90
|
+
config->tracing = false;
|
175
91
|
}
|
176
92
|
}
|
177
93
|
|
@@ -186,7 +102,7 @@ static void event_hook(VALUE tpval, void *data) {
|
|
186
102
|
if (config->tid != gettid()) return;
|
187
103
|
if (in_fork(config)) {
|
188
104
|
rb_tracepoint_disable(config->tracepoint);
|
189
|
-
config->
|
105
|
+
config->tracing = false;
|
190
106
|
return;
|
191
107
|
}
|
192
108
|
|
@@ -205,32 +121,25 @@ static void event_hook(VALUE tpval, void *data) {
|
|
205
121
|
return;
|
206
122
|
}
|
207
123
|
|
208
|
-
|
209
|
-
bool blacklisted = rejected_path(trace_path.filepath, config);
|
210
|
-
|
211
|
-
rs_tracepoint_t trace = extract_full_tracevals(trace_arg, &trace_path);
|
124
|
+
config->callsite = tracearg_path(trace_arg);
|
212
125
|
|
213
|
-
|
126
|
+
config->caller = rs_stack_peek(&config->stack);
|
214
127
|
|
215
|
-
|
128
|
+
rs_method_desc_t method_desc = called_method_desc(trace_arg);
|
129
|
+
rs_stack_push(&config->stack, (rs_stack_frame_t){.method = method_desc});
|
216
130
|
|
217
|
-
|
218
|
-
rs_stack_frame_t *caller_frame = rs_stack_below(&config->stack, stack_frame);
|
219
|
-
log_trace_event_with_caller(config->output_buffer, config->log, stack_frame,
|
220
|
-
caller_frame);
|
131
|
+
rb_funcall(config->trace_proc, id_call, 1, config->self);
|
221
132
|
}
|
222
133
|
|
223
134
|
static void rs_gc_mark(Rotoscope *config) {
|
224
|
-
rb_gc_mark(config->log);
|
225
135
|
rb_gc_mark(config->tracepoint);
|
226
|
-
rb_gc_mark(config->
|
136
|
+
rb_gc_mark(config->trace_proc);
|
227
137
|
rs_stack_mark(&config->stack);
|
228
138
|
}
|
229
139
|
|
230
140
|
void rs_dealloc(Rotoscope *config) {
|
231
141
|
stop_tracing_on_cleanup(config);
|
232
142
|
rs_stack_free(&config->stack);
|
233
|
-
xfree(config->blacklist);
|
234
143
|
xfree(config);
|
235
144
|
}
|
236
145
|
|
@@ -239,12 +148,16 @@ static VALUE rs_alloc(VALUE klass) {
|
|
239
148
|
VALUE self =
|
240
149
|
Data_Make_Struct(klass, Rotoscope, rs_gc_mark, rs_dealloc, config);
|
241
150
|
config->self = self;
|
242
|
-
config->log = Qnil;
|
243
|
-
config->tracepoint = rb_tracepoint_new(Qnil, EVENT_CALL | EVENT_RETURN,
|
244
|
-
event_hook, (void *)config);
|
245
151
|
config->pid = getpid();
|
246
152
|
config->tid = gettid();
|
247
|
-
config->
|
153
|
+
config->tracing = false;
|
154
|
+
config->caller = NULL;
|
155
|
+
config->callsite.filepath = Qnil;
|
156
|
+
config->callsite.lineno = 0;
|
157
|
+
config->trace_proc = Qnil;
|
158
|
+
rs_stack_init(&config->stack, STACK_CAPACITY);
|
159
|
+
config->tracepoint = rb_tracepoint_new(Qnil, EVENT_CALL | EVENT_RETURN,
|
160
|
+
event_hook, (void *)config);
|
248
161
|
return self;
|
249
162
|
}
|
250
163
|
|
@@ -254,133 +167,154 @@ static Rotoscope *get_config(VALUE self) {
|
|
254
167
|
return config;
|
255
168
|
}
|
256
169
|
|
257
|
-
|
258
|
-
|
170
|
+
VALUE rotoscope_initialize(VALUE self) {
|
171
|
+
Rotoscope *config = get_config(self);
|
172
|
+
config->trace_proc = rb_block_proc();
|
173
|
+
return self;
|
174
|
+
}
|
259
175
|
|
260
|
-
|
261
|
-
|
176
|
+
VALUE rotoscope_start_trace(VALUE self) {
|
177
|
+
Rotoscope *config = get_config(self);
|
178
|
+
rb_tracepoint_enable(config->tracepoint);
|
179
|
+
config->tracing = true;
|
180
|
+
return Qnil;
|
181
|
+
}
|
262
182
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
183
|
+
VALUE rotoscope_stop_trace(VALUE self) {
|
184
|
+
Rotoscope *config = get_config(self);
|
185
|
+
if (rb_tracepoint_enabled_p(config->tracepoint)) {
|
186
|
+
rb_tracepoint_disable(config->tracepoint);
|
187
|
+
config->tracing = false;
|
188
|
+
rs_stack_reset(&config->stack);
|
267
189
|
}
|
268
190
|
|
269
|
-
|
270
|
-
config->blacklist_size = RARRAY_LEN(blacklist);
|
271
|
-
char *str = (char *)(config->blacklist + config->blacklist_size);
|
272
|
-
|
273
|
-
for (unsigned long i = 0; i < config->blacklist_size; i++) {
|
274
|
-
VALUE ruby_string = RARRAY_AREF(blacklist, i);
|
275
|
-
|
276
|
-
config->blacklist[i] = str;
|
277
|
-
memcpy(str, RSTRING_PTR(ruby_string), RSTRING_LEN(ruby_string));
|
278
|
-
str += RSTRING_LEN(ruby_string);
|
279
|
-
*str = '\0';
|
280
|
-
str++;
|
281
|
-
}
|
191
|
+
return Qnil;
|
282
192
|
}
|
283
193
|
|
284
|
-
VALUE
|
194
|
+
VALUE rotoscope_tracing_p(VALUE self) {
|
285
195
|
Rotoscope *config = get_config(self);
|
286
|
-
|
196
|
+
return config->tracing ? Qtrue : Qfalse;
|
197
|
+
}
|
287
198
|
|
288
|
-
|
199
|
+
VALUE rotoscope_receiver(VALUE self) {
|
200
|
+
Rotoscope *config = get_config(self);
|
201
|
+
return rb_tracearg_self(rb_tracearg_from_tracepoint(config->tracepoint));
|
202
|
+
}
|
289
203
|
|
290
|
-
|
291
|
-
|
204
|
+
VALUE rotoscope_receiver_class(VALUE self) {
|
205
|
+
Rotoscope *config = get_config(self);
|
206
|
+
rs_stack_frame_t *call = rs_stack_peek(&config->stack);
|
207
|
+
if (call == NULL) {
|
208
|
+
return Qnil;
|
292
209
|
}
|
210
|
+
return rs_method_class(&call->method);
|
211
|
+
}
|
293
212
|
|
294
|
-
|
213
|
+
VALUE rotoscope_receiver_class_name(VALUE self) {
|
214
|
+
VALUE klass = rotoscope_receiver_class(self);
|
215
|
+
if (klass == Qnil) {
|
216
|
+
return Qnil;
|
217
|
+
}
|
218
|
+
return class2str(klass);
|
219
|
+
}
|
295
220
|
|
296
|
-
|
221
|
+
VALUE rotoscope_method_name(VALUE self) {
|
222
|
+
Rotoscope *config = get_config(self);
|
223
|
+
rs_stack_frame_t *call = rs_stack_peek(&config->stack);
|
224
|
+
if (call == NULL) {
|
225
|
+
return Qnil;
|
226
|
+
}
|
227
|
+
return rb_sym2str(call->method.id);
|
228
|
+
}
|
297
229
|
|
298
|
-
|
299
|
-
config
|
300
|
-
|
301
|
-
|
230
|
+
VALUE rotoscope_singleton_method_p(VALUE self) {
|
231
|
+
Rotoscope *config = get_config(self);
|
232
|
+
rs_stack_frame_t *call = rs_stack_peek(&config->stack);
|
233
|
+
if (call == NULL) {
|
234
|
+
return Qnil;
|
235
|
+
}
|
236
|
+
return call->method.singleton_p ? Qtrue : Qfalse;
|
302
237
|
}
|
303
238
|
|
304
|
-
VALUE
|
239
|
+
VALUE rotoscope_caller_object(VALUE self) {
|
305
240
|
Rotoscope *config = get_config(self);
|
306
|
-
|
307
|
-
|
308
|
-
|
241
|
+
if (config->caller == NULL) {
|
242
|
+
return Qnil;
|
243
|
+
}
|
244
|
+
return config->caller->method.receiver;
|
309
245
|
}
|
310
246
|
|
311
|
-
VALUE
|
247
|
+
VALUE rotoscope_caller_class(VALUE self) {
|
312
248
|
Rotoscope *config = get_config(self);
|
313
|
-
if (
|
314
|
-
|
315
|
-
config->state = RS_OPEN;
|
316
|
-
rs_stack_reset(&config->stack);
|
249
|
+
if (config->caller == NULL) {
|
250
|
+
return Qnil;
|
317
251
|
}
|
252
|
+
return rs_method_class(&config->caller->method);
|
253
|
+
}
|
318
254
|
|
319
|
-
|
255
|
+
VALUE rotoscope_caller_class_name(VALUE self) {
|
256
|
+
VALUE klass = rotoscope_caller_class(self);
|
257
|
+
if (klass == Qnil) {
|
258
|
+
return Qnil;
|
259
|
+
}
|
260
|
+
return class2str(klass);
|
320
261
|
}
|
321
262
|
|
322
|
-
VALUE
|
263
|
+
VALUE rotoscope_caller_method_name(VALUE self) {
|
323
264
|
Rotoscope *config = get_config(self);
|
324
|
-
if (config->
|
325
|
-
return
|
265
|
+
if (config->caller == NULL) {
|
266
|
+
return Qnil;
|
326
267
|
}
|
327
|
-
|
328
|
-
config->state = RS_OPEN;
|
329
|
-
if (!in_fork(config)) {
|
330
|
-
rb_funcall(config->log, id_close, 0);
|
331
|
-
}
|
332
|
-
config->state = RS_CLOSED;
|
333
|
-
return Qtrue;
|
268
|
+
return rb_sym2str(config->caller->method.id);
|
334
269
|
}
|
335
270
|
|
336
|
-
VALUE
|
271
|
+
VALUE rotoscope_caller_singleton_method_p(VALUE self) {
|
337
272
|
Rotoscope *config = get_config(self);
|
338
|
-
|
273
|
+
if (config->caller == NULL) {
|
274
|
+
return Qnil;
|
275
|
+
}
|
276
|
+
return config->caller->method.singleton_p ? Qtrue : Qfalse;
|
339
277
|
}
|
340
278
|
|
341
|
-
VALUE
|
342
|
-
|
343
|
-
return
|
279
|
+
VALUE rotoscope_caller_path(VALUE self) {
|
280
|
+
Rotoscope *config = get_config(self);
|
281
|
+
return config->callsite.filepath;
|
344
282
|
}
|
345
283
|
|
346
|
-
VALUE
|
284
|
+
VALUE rotoscope_caller_lineno(VALUE self) {
|
347
285
|
Rotoscope *config = get_config(self);
|
348
|
-
|
349
|
-
case RS_OPEN:
|
350
|
-
return ID2SYM(rb_intern("open"));
|
351
|
-
case RS_TRACING:
|
352
|
-
return ID2SYM(rb_intern("tracing"));
|
353
|
-
default:
|
354
|
-
return ID2SYM(rb_intern("closed"));
|
355
|
-
}
|
286
|
+
return UINT2NUM(config->callsite.lineno);
|
356
287
|
}
|
357
288
|
|
358
289
|
void Init_rotoscope(void) {
|
359
290
|
cTracePoint = rb_const_get(rb_cObject, rb_intern("TracePoint"));
|
360
291
|
|
361
292
|
id_initialize = rb_intern("initialize");
|
362
|
-
|
363
|
-
id_close = rb_intern("close");
|
364
|
-
|
365
|
-
str_quote = rb_str_new_literal("\"");
|
366
|
-
rb_global_variable(&str_quote);
|
367
|
-
str_escaped_quote = rb_str_new_literal("\"\"");
|
368
|
-
rb_global_variable(&str_escaped_quote);
|
369
|
-
|
370
|
-
str_header = rb_str_new_literal(RS_CSV_HEADER "\n");
|
371
|
-
rb_global_variable(&str_header);
|
293
|
+
id_call = rb_intern("call");
|
372
294
|
|
373
295
|
cRotoscope = rb_define_class("Rotoscope", rb_cObject);
|
374
296
|
rb_define_alloc_func(cRotoscope, rs_alloc);
|
375
|
-
rb_define_method(cRotoscope, "initialize",
|
376
|
-
rb_define_method(cRotoscope, "
|
377
|
-
rb_define_method(cRotoscope, "
|
378
|
-
rb_define_method(cRotoscope, "
|
379
|
-
rb_define_method(cRotoscope, "
|
380
|
-
|
381
|
-
rb_define_method(cRotoscope, "
|
382
|
-
|
383
|
-
rb_define_method(cRotoscope, "
|
297
|
+
rb_define_method(cRotoscope, "initialize", rotoscope_initialize, 0);
|
298
|
+
rb_define_method(cRotoscope, "start_trace", rotoscope_start_trace, 0);
|
299
|
+
rb_define_method(cRotoscope, "stop_trace", rotoscope_stop_trace, 0);
|
300
|
+
rb_define_method(cRotoscope, "tracing?", rotoscope_tracing_p, 0);
|
301
|
+
rb_define_method(cRotoscope, "receiver", rotoscope_receiver, 0);
|
302
|
+
rb_define_method(cRotoscope, "receiver_class", rotoscope_receiver_class, 0);
|
303
|
+
rb_define_method(cRotoscope, "receiver_class_name",
|
304
|
+
rotoscope_receiver_class_name, 0);
|
305
|
+
rb_define_method(cRotoscope, "method_name", rotoscope_method_name, 0);
|
306
|
+
rb_define_method(cRotoscope, "singleton_method?",
|
307
|
+
rotoscope_singleton_method_p, 0);
|
308
|
+
rb_define_method(cRotoscope, "caller_object", rotoscope_caller_object, 0);
|
309
|
+
rb_define_method(cRotoscope, "caller_class", rotoscope_caller_class, 0);
|
310
|
+
rb_define_method(cRotoscope, "caller_class_name", rotoscope_caller_class_name,
|
311
|
+
0);
|
312
|
+
rb_define_method(cRotoscope, "caller_method_name",
|
313
|
+
rotoscope_caller_method_name, 0);
|
314
|
+
rb_define_method(cRotoscope, "caller_singleton_method?",
|
315
|
+
rotoscope_caller_singleton_method_p, 0);
|
316
|
+
rb_define_method(cRotoscope, "caller_path", rotoscope_caller_path, 0);
|
317
|
+
rb_define_method(cRotoscope, "caller_lineno", rotoscope_caller_lineno, 0);
|
384
318
|
|
385
319
|
init_callsite();
|
386
320
|
}
|
data/ext/rotoscope/rotoscope.h
CHANGED
@@ -7,51 +7,18 @@
|
|
7
7
|
#define EVENT_CALL (RUBY_EVENT_CALL | RUBY_EVENT_C_CALL)
|
8
8
|
#define EVENT_RETURN (RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN)
|
9
9
|
|
10
|
-
#define CLASS_METHOD "class"
|
11
|
-
#define INSTANCE_METHOD "instance"
|
12
|
-
|
13
10
|
#define STACK_CAPACITY 500
|
14
|
-
#define LOG_BUFFER_SIZE 1000
|
15
|
-
|
16
|
-
// clang-format off
|
17
|
-
|
18
|
-
#define RS_CSV_HEADER \
|
19
|
-
"entity,caller_entity,filepath,lineno,method_name,method_level,caller_method_name,caller_method_level"
|
20
|
-
#define RS_CSV_FORMAT "\"%s\",\"%s\",\"%s\",%d,\"%s\",%s,\"%s\",%s"
|
21
|
-
#define RS_CSV_VALUES(trace, caller_trace, method_name, caller_method_name) \
|
22
|
-
StringValueCStr((trace)->entity), \
|
23
|
-
StringValueCStr((caller_trace)->entity), \
|
24
|
-
StringValueCStr((trace)->filepath), \
|
25
|
-
(trace)->lineno, \
|
26
|
-
StringValueCStr(method_name), \
|
27
|
-
(trace)->method_level, \
|
28
|
-
StringValueCStr(caller_method_name), \
|
29
|
-
(caller_trace)->method_level
|
30
|
-
|
31
|
-
// clang-format on
|
32
|
-
|
33
|
-
typedef enum {
|
34
|
-
RS_CLOSED = 0,
|
35
|
-
RS_OPEN,
|
36
|
-
RS_TRACING,
|
37
|
-
} rs_state;
|
38
11
|
|
39
12
|
typedef struct {
|
40
13
|
VALUE self;
|
41
|
-
VALUE log;
|
42
14
|
VALUE tracepoint;
|
43
|
-
const char **blacklist;
|
44
|
-
unsigned long blacklist_size;
|
45
15
|
pid_t pid;
|
46
16
|
unsigned long tid;
|
47
|
-
|
17
|
+
bool tracing;
|
48
18
|
rs_stack_t stack;
|
49
|
-
|
19
|
+
rs_stack_frame_t *caller;
|
20
|
+
rs_callsite_t callsite;
|
21
|
+
VALUE trace_proc;
|
50
22
|
} Rotoscope;
|
51
23
|
|
52
|
-
typedef struct {
|
53
|
-
VALUE name;
|
54
|
-
const char *method_level;
|
55
|
-
} rs_class_desc_t;
|
56
|
-
|
57
24
|
#endif
|
data/ext/rotoscope/stack.c
CHANGED
@@ -3,19 +3,6 @@
|
|
3
3
|
#include <stdlib.h>
|
4
4
|
#include <string.h>
|
5
5
|
#include "ruby.h"
|
6
|
-
#include "tracepoint.h"
|
7
|
-
|
8
|
-
static void insert_root_node(rs_stack_t *stack) {
|
9
|
-
VALUE rb_unknown_str = rb_str_new_cstr(UNKNOWN_STR);
|
10
|
-
rs_tracepoint_t root_trace =
|
11
|
-
(rs_tracepoint_t){.event = UNKNOWN_STR,
|
12
|
-
.entity = rb_str_new_cstr("<ROOT>"),
|
13
|
-
.filepath = rb_unknown_str,
|
14
|
-
.method_name = rb_unknown_str,
|
15
|
-
.method_level = UNKNOWN_STR,
|
16
|
-
.lineno = 0};
|
17
|
-
rs_stack_push(stack, root_trace, false);
|
18
|
-
}
|
19
6
|
|
20
7
|
static void resize_buffer(rs_stack_t *stack) {
|
21
8
|
unsigned int newsize = stack->capacity * 2;
|
@@ -31,17 +18,12 @@ bool rs_stack_full(rs_stack_t *stack) {
|
|
31
18
|
|
32
19
|
bool rs_stack_empty(rs_stack_t *stack) { return stack->top < 0; }
|
33
20
|
|
34
|
-
|
35
|
-
bool blacklisted) {
|
21
|
+
void rs_stack_push(rs_stack_t *stack, rs_stack_frame_t new_frame) {
|
36
22
|
if (rs_stack_full(stack)) {
|
37
23
|
resize_buffer(stack);
|
38
24
|
}
|
39
25
|
|
40
|
-
rs_stack_frame_t new_frame =
|
41
|
-
(rs_stack_frame_t){.tp = trace, .blacklisted = blacklisted};
|
42
|
-
|
43
26
|
stack->contents[++stack->top] = new_frame;
|
44
|
-
return new_frame;
|
45
27
|
}
|
46
28
|
|
47
29
|
rs_stack_frame_t rs_stack_pop(rs_stack_t *stack) {
|
@@ -55,29 +37,13 @@ rs_stack_frame_t rs_stack_pop(rs_stack_t *stack) {
|
|
55
37
|
|
56
38
|
rs_stack_frame_t *rs_stack_peek(rs_stack_t *stack) {
|
57
39
|
if (rs_stack_empty(stack)) {
|
58
|
-
|
59
|
-
exit(1);
|
40
|
+
return NULL;
|
60
41
|
}
|
61
42
|
|
62
43
|
return &stack->contents[stack->top];
|
63
44
|
}
|
64
45
|
|
65
|
-
|
66
|
-
if (frame < stack->contents || frame > stack->contents + stack->top) {
|
67
|
-
fprintf(stderr, "Invalid stack frame (bottom: %p, frame: %p, top: %p)\n",
|
68
|
-
stack->contents, frame, &stack->contents[stack->top]);
|
69
|
-
exit(1);
|
70
|
-
} else if (stack->contents == frame) {
|
71
|
-
return NULL;
|
72
|
-
} else {
|
73
|
-
return frame - 1;
|
74
|
-
}
|
75
|
-
}
|
76
|
-
|
77
|
-
void rs_stack_reset(rs_stack_t *stack) {
|
78
|
-
stack->top = -1;
|
79
|
-
insert_root_node(stack);
|
80
|
-
}
|
46
|
+
void rs_stack_reset(rs_stack_t *stack) { stack->top = -1; }
|
81
47
|
|
82
48
|
void rs_stack_free(rs_stack_t *stack) {
|
83
49
|
xfree(stack->contents);
|
@@ -91,13 +57,11 @@ void rs_stack_init(rs_stack_t *stack, unsigned int capacity) {
|
|
91
57
|
stack->contents = contents;
|
92
58
|
stack->capacity = capacity;
|
93
59
|
stack->top = -1;
|
94
|
-
|
95
|
-
insert_root_node(stack);
|
96
60
|
}
|
97
61
|
|
98
62
|
void rs_stack_mark(rs_stack_t *stack) {
|
99
63
|
for (int i = 0; i <= stack->top; i++) {
|
100
|
-
rs_stack_frame_t frame = stack->contents[i];
|
101
|
-
|
64
|
+
rs_stack_frame_t *frame = &stack->contents[i];
|
65
|
+
rs_method_desc_mark(&frame->method);
|
102
66
|
}
|
103
67
|
}
|
data/ext/rotoscope/stack.h
CHANGED
@@ -1,14 +1,9 @@
|
|
1
1
|
#ifndef _INC_ROTOSCOPE_STACK_H_
|
2
2
|
#define _INC_ROTOSCOPE_STACK_H_
|
3
3
|
#include <stdbool.h>
|
4
|
-
#include "
|
4
|
+
#include "method_desc.h"
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
typedef struct rs_stack_frame_t {
|
9
|
-
struct rs_tracepoint_t tp;
|
10
|
-
bool blacklisted;
|
11
|
-
} rs_stack_frame_t;
|
6
|
+
typedef struct { rs_method_desc_t method; } rs_stack_frame_t;
|
12
7
|
|
13
8
|
typedef struct {
|
14
9
|
int capacity;
|
@@ -19,12 +14,11 @@ typedef struct {
|
|
19
14
|
void rs_stack_init(rs_stack_t *stack, unsigned int capacity);
|
20
15
|
void rs_stack_reset(rs_stack_t *stack);
|
21
16
|
void rs_stack_free(rs_stack_t *stack);
|
22
|
-
|
17
|
+
void rs_stack_push(rs_stack_t *stack, rs_stack_frame_t frame);
|
23
18
|
bool rs_stack_empty(rs_stack_t *stack);
|
24
19
|
bool rs_stack_full(rs_stack_t *stack);
|
25
20
|
rs_stack_frame_t rs_stack_pop(rs_stack_t *stack);
|
26
21
|
rs_stack_frame_t *rs_stack_peek(rs_stack_t *stack);
|
27
|
-
rs_stack_frame_t *rs_stack_below(rs_stack_t *stack, rs_stack_frame_t *frame);
|
28
22
|
void rs_stack_mark(rs_stack_t *stack);
|
29
23
|
|
30
24
|
#endif
|
data/lib/rotoscope.rb
CHANGED
@@ -1,70 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'rotoscope/rotoscope'
|
3
|
-
require 'csv'
|
4
3
|
|
5
4
|
class Rotoscope
|
6
|
-
|
7
|
-
def new(output, blacklist: [])
|
8
|
-
if output.is_a?(String)
|
9
|
-
io = File.open(output, 'w')
|
10
|
-
prevent_flush_from_finalizer_in_fork(io)
|
11
|
-
obj = super(io, blacklist)
|
12
|
-
obj.log_path = output
|
13
|
-
obj
|
14
|
-
else
|
15
|
-
super(output, blacklist)
|
16
|
-
end
|
17
|
-
end
|
5
|
+
autoload :CallLogger, 'rotoscope/call_logger'
|
18
6
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
rs
|
23
|
-
ensure
|
24
|
-
rs.close if rs && dest.is_a?(String)
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def prevent_flush_from_finalizer_in_fork(io)
|
30
|
-
pid = Process.pid
|
31
|
-
finalizer = lambda do |_|
|
32
|
-
next if Process.pid == pid
|
33
|
-
# close the file descriptor from another IO object so
|
34
|
-
# buffered writes aren't flushed
|
35
|
-
IO.for_fd(io.fileno).close
|
36
|
-
end
|
37
|
-
ObjectSpace.define_finalizer(io, finalizer)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
attr_accessor :log_path
|
42
|
-
|
43
|
-
def mark(message = "")
|
44
|
-
state = self.state
|
45
|
-
if state == :tracing
|
46
|
-
# stop tracing to avoid logging these io method calls
|
47
|
-
stop_trace
|
48
|
-
end
|
49
|
-
io.write("--- ")
|
50
|
-
io.puts(message)
|
7
|
+
def trace
|
8
|
+
start_trace
|
9
|
+
yield
|
51
10
|
ensure
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
def closed?
|
56
|
-
state == :closed
|
57
|
-
end
|
58
|
-
|
59
|
-
def inspect
|
60
|
-
"Rotoscope(state: #{state}, log_path: \"#{short_log_path}\", object_id: #{format('0x00%x', object_id << 1)})"
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def short_log_path
|
66
|
-
return log_path if log_path.length < 40
|
67
|
-
chars = log_path.chars
|
68
|
-
"#{chars.first(17).join('')}...#{chars.last(20).join('')}"
|
11
|
+
stop_trace
|
69
12
|
end
|
70
13
|
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'csv'
|
4
|
+
|
5
|
+
class Rotoscope
|
6
|
+
class CallLogger
|
7
|
+
class << self
|
8
|
+
def trace(dest, blacklist: [])
|
9
|
+
rs = new(dest, blacklist: blacklist)
|
10
|
+
rs.trace { yield rs }
|
11
|
+
rs
|
12
|
+
ensure
|
13
|
+
rs.io.close if rs && dest.is_a?(String)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
HEADER = "entity,caller_entity,filepath,lineno,method_name,method_level,caller_method_name,caller_method_level\n"
|
18
|
+
|
19
|
+
attr_reader :io, :blacklist
|
20
|
+
|
21
|
+
def initialize(output = nil, blacklist: nil)
|
22
|
+
unless blacklist.is_a?(Regexp)
|
23
|
+
blacklist = Regexp.union(blacklist || [])
|
24
|
+
end
|
25
|
+
@blacklist = blacklist
|
26
|
+
|
27
|
+
if output.is_a?(String)
|
28
|
+
@io = File.open(output, 'w')
|
29
|
+
prevent_flush_from_finalizer_in_fork(@io)
|
30
|
+
else
|
31
|
+
@io = output
|
32
|
+
end
|
33
|
+
@output_buffer = ''.dup
|
34
|
+
@pid = Process.pid
|
35
|
+
@thread = Thread.current
|
36
|
+
|
37
|
+
@io << HEADER
|
38
|
+
|
39
|
+
@rotoscope = Rotoscope.new(&method(:log_call))
|
40
|
+
end
|
41
|
+
|
42
|
+
def trace
|
43
|
+
start_trace
|
44
|
+
yield
|
45
|
+
ensure
|
46
|
+
@rotoscope.stop_trace
|
47
|
+
end
|
48
|
+
|
49
|
+
def start_trace
|
50
|
+
@rotoscope.start_trace
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop_trace
|
54
|
+
@rotoscope.stop_trace
|
55
|
+
end
|
56
|
+
|
57
|
+
def mark(message = "")
|
58
|
+
was_tracing = @rotoscope.tracing?
|
59
|
+
if was_tracing
|
60
|
+
# stop tracing to avoid logging these io method calls
|
61
|
+
@rotoscope.stop_trace
|
62
|
+
end
|
63
|
+
if @pid == Process.pid && @thread == Thread.current
|
64
|
+
@io.write("--- ")
|
65
|
+
@io.puts(message)
|
66
|
+
end
|
67
|
+
ensure
|
68
|
+
@rotoscope.start_trace if was_tracing
|
69
|
+
end
|
70
|
+
|
71
|
+
def close
|
72
|
+
@rotoscope.stop_trace
|
73
|
+
if @pid == Process.pid && @thread == Thread.current
|
74
|
+
@io.close
|
75
|
+
end
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
def closed?
|
80
|
+
@io.closed?
|
81
|
+
end
|
82
|
+
|
83
|
+
def state
|
84
|
+
return :closed if io.closed?
|
85
|
+
@rotoscope.tracing? ? :tracing : :open
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def log_call(call)
|
91
|
+
return if blacklist.match?(call.caller_path)
|
92
|
+
return if self == call.receiver
|
93
|
+
|
94
|
+
if call.caller_method_name.nil?
|
95
|
+
caller_class_name = '<ROOT>'
|
96
|
+
caller_method_name = '<UNKNOWN>'
|
97
|
+
caller_method_level = '<UNKNOWN>'
|
98
|
+
else
|
99
|
+
caller_class_name = call.caller_class_name
|
100
|
+
caller_method_name = escape_csv_string(call.caller_method_name)
|
101
|
+
caller_method_level = call.caller_singleton_method? ? 'class' : 'instance'
|
102
|
+
end
|
103
|
+
|
104
|
+
call_method_level = call.singleton_method? ? 'class' : 'instance'
|
105
|
+
method_name = escape_csv_string(call.method_name)
|
106
|
+
|
107
|
+
buffer = @output_buffer
|
108
|
+
buffer.clear
|
109
|
+
buffer <<
|
110
|
+
'"' << call.receiver_class_name << '",' \
|
111
|
+
'"' << caller_class_name << '",' \
|
112
|
+
'"' << call.caller_path << '",' \
|
113
|
+
<< call.caller_lineno.to_s << ',' \
|
114
|
+
'"' << method_name << '",' \
|
115
|
+
<< call_method_level << ',' \
|
116
|
+
'"' << caller_method_name << '",' \
|
117
|
+
<< caller_method_level << "\n"
|
118
|
+
io.write(buffer)
|
119
|
+
end
|
120
|
+
|
121
|
+
def escape_csv_string(string)
|
122
|
+
string.include?('"') ? string.gsub('"', '""') : string
|
123
|
+
end
|
124
|
+
|
125
|
+
def prevent_flush_from_finalizer_in_fork(io)
|
126
|
+
pid = Process.pid
|
127
|
+
finalizer = lambda do |_|
|
128
|
+
next if Process.pid == pid
|
129
|
+
# close the file descriptor from another IO object so
|
130
|
+
# buffered writes aren't flushed
|
131
|
+
IO.for_fd(io.fileno).close
|
132
|
+
end
|
133
|
+
ObjectSpace.define_finalizer(io, finalizer)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/lib/rotoscope/version.rb
CHANGED
data/rotoscope.gemspec
CHANGED
@@ -25,5 +25,5 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.add_development_dependency 'rake-compiler', '~> 0.9'
|
26
26
|
s.add_development_dependency 'mocha', '~> 0.14'
|
27
27
|
s.add_development_dependency 'minitest', '~> 5.0'
|
28
|
-
s.add_development_dependency 'rubocop', '~> 0.
|
28
|
+
s.add_development_dependency 'rubocop', '~> 0.56'
|
29
29
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rotoscope
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.0.pre.
|
4
|
+
version: 0.3.0.pre.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jahfer Husain
|
@@ -59,14 +59,14 @@ dependencies:
|
|
59
59
|
requirements:
|
60
60
|
- - "~>"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: '0.
|
62
|
+
version: '0.56'
|
63
63
|
type: :development
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: '0.
|
69
|
+
version: '0.56'
|
70
70
|
description: High-performance logger of Ruby method invocations
|
71
71
|
email: jahfer.husain@shopify.com
|
72
72
|
executables: []
|
@@ -88,13 +88,14 @@ files:
|
|
88
88
|
- ext/rotoscope/callsite.c
|
89
89
|
- ext/rotoscope/callsite.h
|
90
90
|
- ext/rotoscope/extconf.rb
|
91
|
+
- ext/rotoscope/method_desc.c
|
92
|
+
- ext/rotoscope/method_desc.h
|
91
93
|
- ext/rotoscope/rotoscope.c
|
92
94
|
- ext/rotoscope/rotoscope.h
|
93
95
|
- ext/rotoscope/stack.c
|
94
96
|
- ext/rotoscope/stack.h
|
95
|
-
- ext/rotoscope/tracepoint.c
|
96
|
-
- ext/rotoscope/tracepoint.h
|
97
97
|
- lib/rotoscope.rb
|
98
|
+
- lib/rotoscope/call_logger.rb
|
98
99
|
- lib/rotoscope/version.rb
|
99
100
|
- rotoscope.gemspec
|
100
101
|
homepage: https://github.com/shopify/rotoscope
|
data/ext/rotoscope/tracepoint.c
DELETED
data/ext/rotoscope/tracepoint.h
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
#ifndef _INC_ROTOSCOPE_TRACEPOINT_H_
|
2
|
-
#define _INC_ROTOSCOPE_TRACEPOINT_H_
|
3
|
-
|
4
|
-
#include "ruby.h"
|
5
|
-
|
6
|
-
typedef struct rs_tracepoint_t {
|
7
|
-
const char *event;
|
8
|
-
VALUE entity;
|
9
|
-
VALUE filepath;
|
10
|
-
VALUE method_name;
|
11
|
-
const char *method_level;
|
12
|
-
unsigned int lineno;
|
13
|
-
} rs_tracepoint_t;
|
14
|
-
|
15
|
-
void rs_tracepoint_mark(rs_tracepoint_t *tracepoint);
|
16
|
-
|
17
|
-
#endif
|