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 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