rotoscope 0.3.0.pre.7 → 0.3.0.pre.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dc57c07b922488935b47e583d422a36d9fa32096
4
- data.tar.gz: cd70207ab9f61bcd490506a5defc6c293dbec11d
3
+ metadata.gz: a13014ea4da609b9a22d6abf8bbd57d6f2d33ffe
4
+ data.tar.gz: 1d3c71e1673bfc4cc4adfec7e5041e66f0104999
5
5
  SHA512:
6
- metadata.gz: 7c512251290df7ee37e72e0f130ca879da887f3ecdd206b60e9e4a361e422bf68daf6b6c42acbb1c20a40efaa4c7166334fc2e21d0df2e3dc1f9c722267b31c0
7
- data.tar.gz: a5e1d3bdd7ae63147097154630aae6bb9c4153fa110be4fc1873cbce38f7d684a3e0f87cc1c80f4dfd811c04e21d21c8dcc5adad7de4afc91a977dc266ac0d41
6
+ metadata.gz: a2acdaacb7ee98c49c437ffd73e6444dbbf0c0b8dbeedad1edef839c3dde20ff9344a331b1747be6a4a306855dd818a094768dd16d091a41b70a1ff39636cd0d
7
+ data.tar.gz: f2e35df44720ea2c7d53c5c8c548b99867772d02fa5c6cd56d2b863d982c4635840d2c74b328c05053718aa264746d0f450f2e4168aa6a30bca4b377234160e7
@@ -1 +1 @@
1
- 2.3.3
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/flattened_dog.rb,19,<ROOT>,<UNKNOWN>,<UNKNOWN>
77
- Dog,initialize,instance,example/flattened_dog.rb,19,Dog,new,class
78
- Dog,bark,instance,example/flattened_dog.rb,20,<ROOT>,<UNKNOWN>,<UNKNOWN>
79
- Noisemaker,speak,class,example/flattened_dog.rb,5,Dog,bark,instance
80
- Noisemaker,puts,class,example/flattened_dog.rb,11,Noisemaker,speak,class
81
- IO,puts,instance,example/flattened_dog.rb,11,Noisemaker,puts,class
82
- IO,write,instance,example/flattened_dog.rb,11,IO,puts,instance
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`](#rotoscopetracedest-blacklist--flatten-false)
89
- - [`new`](#rotoscopenewdest-blacklist--flatten-false)
56
+ - [`trace`](#rotoscopecallloggertracedest-blacklist-)
57
+ - [`new`](#rotoscopecallloggernewdest-blacklist-)
90
58
  - [Public Instance Methods](#public-instance-methods)
91
- - [`trace`](#rotoscopetraceblock)
92
- - [`start_trace`](#rotoscopestart_trace)
93
- - [`stop_trace`](#rotoscopestop_trace)
94
- - [`mark`](#rotoscopemarkstr--)
95
- - [`close`](#rotoscopeclose)
96
- - [`state`](#rotoscopestate)
97
- - [`closed?`](#rotoscopeclosed)
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: [], flatten: false)`
71
+ #### `Rotoscope::CallLogger::trace(dest, blacklist: [])`
105
72
 
106
- Writes all calls and returns of methods to `dest`, except for those whose filepath contains any entry in `blacklist`. `dest` is either a filename or an `IO`. The `flatten` option reduces the output data to a deduplicated list of method invocations and their caller, instead of all `call` and `return` events. Methods invoked at the top of the trace will have a caller entity of `<ROOT>` and a caller method name of `<UNKNOWN>`.
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/"], flatten: true) { |rs| ... }
78
+ Rotoscope::CallLogger.trace(dest, blacklist: ["/.gem/"]) { |rs| ... }
112
79
  ```
113
80
 
114
- #### `Rotoscope::new(dest, blacklist: [], flatten: false)`
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/"], flatten: true)
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 and returns to the `dest` specified in the initializer.
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
@@ -35,4 +35,9 @@ Rake::TestTask.new 'test' do |t|
35
35
  end
36
36
  task test: :build
37
37
 
38
- task default: :test
38
+ task :rubocop do
39
+ require 'rubocop/rake_task'
40
+ RuboCop::RakeTask.new
41
+ end
42
+
43
+ task default: [:test, :rubocop]
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 lib/rotoscope/*.h -o -iname *.c | xargs clang-format -i -style=file
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
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: rotoscope
3
3
  up:
4
- - ruby: 2.3.3
4
+ - ruby: 2.4.3
5
5
  - homebrew:
6
6
  - clang-format
7
7
  - bundler
@@ -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
@@ -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, id_gsub, id_close;
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 rs_class_desc_t tracearg_class(rb_trace_arg_t *trace_arg) {
79
- VALUE klass;
80
- const char *method_level;
81
- VALUE self = rb_tracearg_self(trace_arg);
82
-
83
- if ((RB_TYPE_P(self, T_CLASS) || RB_TYPE_P(self, T_MODULE)) &&
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 (rs_class_desc_t){
93
- .name = class2str(klass), .method_level = method_level,
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->state == RS_TRACING) {
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->state = RS_OPEN;
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->state = RS_OPEN;
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
- rs_callsite_t trace_path = tracearg_path(trace_arg);
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
- rs_stack_push(&config->stack, trace, blacklisted);
126
+ config->caller = rs_stack_peek(&config->stack);
214
127
 
215
- if (blacklisted) return;
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
- rs_stack_frame_t *stack_frame = rs_stack_peek(&config->stack);
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->output_buffer);
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->output_buffer = Qnil;
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
- void copy_blacklist(Rotoscope *config, VALUE blacklist) {
258
- Check_Type(blacklist, T_ARRAY);
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
- size_t blacklist_malloc_size =
261
- RARRAY_LEN(blacklist) * sizeof(*config->blacklist);
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
- for (long i = 0; i < RARRAY_LEN(blacklist); i++) {
264
- VALUE ruby_string = RARRAY_AREF(blacklist, i);
265
- Check_Type(ruby_string, T_STRING);
266
- blacklist_malloc_size += RSTRING_LEN(ruby_string) + 1;
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
- config->blacklist = ruby_xmalloc(blacklist_malloc_size);
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 initialize(int argc, VALUE *argv, VALUE self) {
194
+ VALUE rotoscope_tracing_p(VALUE self) {
285
195
  Rotoscope *config = get_config(self);
286
- VALUE output, blacklist;
196
+ return config->tracing ? Qtrue : Qfalse;
197
+ }
287
198
 
288
- rb_scan_args(argc, argv, "11", &output, &blacklist);
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
- if (!NIL_P(blacklist)) {
291
- copy_blacklist(config, blacklist);
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
- config->log = output;
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
- rb_io_write(config->log, str_header);
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
- rs_stack_init(&config->stack, STACK_CAPACITY);
299
- config->state = RS_OPEN;
300
- config->output_buffer = rb_str_buf_new(LOG_BUFFER_SIZE);
301
- return self;
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 rotoscope_start_trace(VALUE self) {
239
+ VALUE rotoscope_caller_object(VALUE self) {
305
240
  Rotoscope *config = get_config(self);
306
- rb_tracepoint_enable(config->tracepoint);
307
- config->state = RS_TRACING;
308
- return Qnil;
241
+ if (config->caller == NULL) {
242
+ return Qnil;
243
+ }
244
+ return config->caller->method.receiver;
309
245
  }
310
246
 
311
- VALUE rotoscope_stop_trace(VALUE self) {
247
+ VALUE rotoscope_caller_class(VALUE self) {
312
248
  Rotoscope *config = get_config(self);
313
- if (rb_tracepoint_enabled_p(config->tracepoint)) {
314
- rb_tracepoint_disable(config->tracepoint);
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
- return Qnil;
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 rotoscope_close(VALUE self) {
263
+ VALUE rotoscope_caller_method_name(VALUE self) {
323
264
  Rotoscope *config = get_config(self);
324
- if (config->state == RS_CLOSED) {
325
- return Qtrue;
265
+ if (config->caller == NULL) {
266
+ return Qnil;
326
267
  }
327
- rb_tracepoint_disable(config->tracepoint);
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 rotoscope_io(VALUE self) {
271
+ VALUE rotoscope_caller_singleton_method_p(VALUE self) {
337
272
  Rotoscope *config = get_config(self);
338
- return config->log;
273
+ if (config->caller == NULL) {
274
+ return Qnil;
275
+ }
276
+ return config->caller->method.singleton_p ? Qtrue : Qfalse;
339
277
  }
340
278
 
341
- VALUE rotoscope_trace(VALUE self) {
342
- rotoscope_start_trace(self);
343
- return rb_ensure(rb_yield, Qundef, rotoscope_stop_trace, self);
279
+ VALUE rotoscope_caller_path(VALUE self) {
280
+ Rotoscope *config = get_config(self);
281
+ return config->callsite.filepath;
344
282
  }
345
283
 
346
- VALUE rotoscope_state(VALUE self) {
284
+ VALUE rotoscope_caller_lineno(VALUE self) {
347
285
  Rotoscope *config = get_config(self);
348
- switch (config->state) {
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
- id_gsub = rb_intern("gsub");
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", initialize, -1);
376
- rb_define_method(cRotoscope, "trace", (VALUE(*)(ANYARGS))rotoscope_trace, 0);
377
- rb_define_method(cRotoscope, "close", (VALUE(*)(ANYARGS))rotoscope_close, 0);
378
- rb_define_method(cRotoscope, "io", rotoscope_io, 0);
379
- rb_define_method(cRotoscope, "start_trace",
380
- (VALUE(*)(ANYARGS))rotoscope_start_trace, 0);
381
- rb_define_method(cRotoscope, "stop_trace",
382
- (VALUE(*)(ANYARGS))rotoscope_stop_trace, 0);
383
- rb_define_method(cRotoscope, "state", (VALUE(*)(ANYARGS))rotoscope_state, 0);
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
  }
@@ -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
- rs_state state;
17
+ bool tracing;
48
18
  rs_stack_t stack;
49
- VALUE output_buffer;
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
@@ -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
- rs_stack_frame_t rs_stack_push(rs_stack_t *stack, rs_tracepoint_t trace,
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
- fprintf(stderr, "Stack is empty!\n");
59
- exit(1);
40
+ return NULL;
60
41
  }
61
42
 
62
43
  return &stack->contents[stack->top];
63
44
  }
64
45
 
65
- rs_stack_frame_t *rs_stack_below(rs_stack_t *stack, rs_stack_frame_t *frame) {
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
- rs_tracepoint_mark(&frame.tp);
64
+ rs_stack_frame_t *frame = &stack->contents[i];
65
+ rs_method_desc_mark(&frame->method);
102
66
  }
103
67
  }
@@ -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 "tracepoint.h"
4
+ #include "method_desc.h"
5
5
 
6
- #define UNKNOWN_STR "<UNKNOWN>"
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
- rs_stack_frame_t rs_stack_push(rs_stack_t *stack, rs_tracepoint_t trace, bool backlisted);
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
@@ -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
- class << self
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
- def trace(dest, blacklist: [])
20
- rs = new(dest, blacklist: blacklist)
21
- rs.trace { yield rs }
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
- start_trace if state == :tracing
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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  class Rotoscope
3
- VERSION = "0.3.0.pre.7"
3
+ VERSION = "0.3.0.pre.8"
4
4
  end
@@ -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.49'
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.7
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.49'
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.49'
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
@@ -1,8 +0,0 @@
1
- #include "tracepoint.h"
2
- #include "ruby.h"
3
-
4
- void rs_tracepoint_mark(rs_tracepoint_t *tracepoint) {
5
- rb_gc_mark(tracepoint->entity);
6
- rb_gc_mark(tracepoint->filepath);
7
- rb_gc_mark(tracepoint->method_name);
8
- }
@@ -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