rotoscope 0.3.0.pre.1 → 0.3.0.pre.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8a94009a6d6ee2a34d124e8b922feb3ff3eda7f2
4
- data.tar.gz: eab31fe889c50ea034197aa1a435ccb22c832630
3
+ metadata.gz: 214bc0f95d10e738330ccf96ad98195588ce2fed
4
+ data.tar.gz: c8be5f0941288a7a5242e830b17c98de16935022
5
5
  SHA512:
6
- metadata.gz: 8ab381c724461c0b5dbab3a8dbbbf3e451c5aae060b8aecbaa9f28abc4d7aa636ecf7cd451b68537e104e13fc53c45243c47e4b44b48875f58bf1e32902a3f58
7
- data.tar.gz: 5a598811ac79f0524d0e2d93d28a727a51e296cb575ed1de9b98e0f1b3921927b8557873e0bdcc682b8f7d09136f6d347c5d6eb2f8a7192bf39355a01e26f228
6
+ metadata.gz: 0af5d811380da0de299478d7176a09308c1c2ff3a54ea77be54df01e4c34213d8208407b30a90ef34b19d07395c34e2b08395db7a2ba0b0eb783dff8c039b5a8
7
+ data.tar.gz: 577ec0a7a6bfc747bc3cde9d80b854aab30cd4804f60ad844273b917741c78f8900d043e87b4eb9a0905884c421e282e0cfe991a62d37ebfef6b7906114bb10d
data/README.md CHANGED
@@ -4,7 +4,7 @@ Rotoscope is a high-performance logger of Ruby method invocations.
4
4
 
5
5
  ## Status
6
6
 
7
- [![status](https://circleci.com/gh/Shopify/rotoscope/tree/master.svg?style=shield&circle-token=cddbd315df7a81ab944adf4dfc14a5800cd589fc)](https://circleci.com/gh/Shopify/rotoscope/tree/master) [![Gem Version](https://badge.fury.io/rb/rotoscope.svg)](https://badge.fury.io/rb/rotoscope)
7
+ [![Build Status](https://travis-ci.org/Shopify/rotoscope.svg?branch=master)](https://travis-ci.org/Shopify/rotoscope) [![Gem Version](https://badge.fury.io/rb/rotoscope.svg)](https://badge.fury.io/rb/rotoscope)
8
8
 
9
9
  Rotoscope is subject to breaking changes in minor versions until `1.0` is available.
10
10
 
data/Rakefile CHANGED
@@ -4,7 +4,9 @@
4
4
  # ==========================================================
5
5
  GEMSPEC = Gem::Specification.load('rotoscope.gemspec')
6
6
 
7
+ require 'bundler/gem_tasks'
7
8
  require 'rubygems/package_task'
9
+
8
10
  Gem::PackageTask.new(GEMSPEC) do |pkg|
9
11
  end
10
12
 
@@ -13,8 +13,8 @@
13
13
  #include "strmemo.h"
14
14
  #include "tracepoint.h"
15
15
 
16
- VALUE cRotoscope, cTracePoint;
17
- ID id_initialize;
16
+ VALUE cRotoscope, cTracePoint, cInstructionSeq;
17
+ ID id_initialize, id_of, id_label;
18
18
 
19
19
  static unsigned long gettid() {
20
20
  return NUM2ULONG(rb_obj_id(rb_thread_current()));
@@ -108,7 +108,6 @@ static rs_tracepoint_t extract_full_tracevals(rb_trace_arg_t *trace_arg,
108
108
 
109
109
  VALUE method_name = tracearg_method_name(trace_arg);
110
110
  VALUE filepath = callsite->filepath;
111
-
112
111
  return (rs_tracepoint_t){.event = evflag2name(event_flag),
113
112
  .entity = method_owner.name,
114
113
  .filepath = filepath,
@@ -119,30 +118,58 @@ static rs_tracepoint_t extract_full_tracevals(rb_trace_arg_t *trace_arg,
119
118
 
120
119
  static bool in_fork(Rotoscope *config) { return config->pid != getpid(); }
121
120
 
122
- static void log_trace_event(FILE *stream, rs_tracepoint_t *trace) {
123
- fprintf(stream, RS_CSV_FORMAT "\n", RS_CSV_VALUES(trace));
121
+ static VALUE unsafe_obj_method(VALUE argv) {
122
+ VALUE *args = (VALUE *)argv;
123
+ return rb_obj_method(args[0], args[1]);
124
124
  }
125
+ static VALUE nil_method(VALUE *args) { return Qnil; }
125
126
 
126
- unsigned char output_buffer[LOG_BUFFER_SIZE];
127
- static void log_trace_event_with_caller(FILE *stream,
128
- rs_stack_frame_t *stack_frame,
129
- rs_strmemo_t **call_memo) {
130
- rs_stack_frame_t *caller = stack_frame->caller;
131
- while (caller->blacklisted) {
132
- caller = caller->caller;
133
- }
127
+ static bool endof_block_method(rb_trace_arg_t *trace_arg) {
128
+ VALUE method_id = rb_tracearg_method_id(trace_arg);
129
+ if (NIL_P(method_id)) return false;
134
130
 
135
- snprintf((char *)output_buffer, LOG_BUFFER_SIZE, RS_FLATTENED_CSV_FORMAT "\n",
136
- RS_FLATTENED_CSV_VALUES(&stack_frame->tp, &caller->tp));
131
+ VALUE self = rb_tracearg_self(trace_arg);
132
+ VALUE argv[2] = {self, method_id};
133
+ VALUE method = rb_rescue2(unsafe_obj_method, (VALUE)&argv, nil_method,
134
+ (VALUE)&argv, rb_eNameError, (VALUE)0);
135
+ if (NIL_P(method)) return false;
136
+
137
+ VALUE iseq = rb_funcall(cInstructionSeq, id_of, 1, method);
138
+ if (!RTEST(iseq)) return false;
137
139
 
138
- if (rs_strmemo_uniq(call_memo, output_buffer)) {
140
+ VALUE label = rb_funcall(iseq, id_label, 0);
141
+ char *label_str = StringValueCStr(label);
142
+ return strncmp(VM_BLOCK_PREFIX, label_str, strlen(VM_BLOCK_PREFIX)) == 0;
143
+ }
144
+
145
+ unsigned char output_buffer[LOG_BUFFER_SIZE];
146
+ static void log_trace_event_with_caller(FILE *stream, rs_strmemo_t **call_memo,
147
+ rs_stack_frame_t frame) {
148
+ snprintf((char *)output_buffer, LOG_BUFFER_SIZE, RS_FLATTENED_CSV_FORMAT "\n",
149
+ RS_FLATTENED_CSV_VALUES(frame));
150
+ if (rs_strmemo_uniq(call_memo, (unsigned char *)output_buffer)) {
139
151
  fputs((char *)output_buffer, stream);
140
152
  }
141
153
  }
142
154
 
155
+ static void log_trace_event(FILE *stream, rs_tracepoint_t trace) {
156
+ fprintf(stream, RS_CSV_FORMAT "\n", RS_CSV_VALUES(trace));
157
+ }
158
+
159
+ static bool invalid_stack_return(rs_stack_t *stack, rs_tracepoint_t *trace) {
160
+ rs_stack_frame_t *last_frame = rs_stack_peek(stack);
161
+ return !rs_raw_tracepoint_cmp(&trace->raw, &last_frame->tp.raw);
162
+ }
163
+
164
+ static void copy_filepath_from_caller(rs_stack_t *stack,
165
+ rs_tracepoint_t *trace) {
166
+ rs_stack_frame_t *last_frame = rs_stack_peek(stack);
167
+ trace->filepath = last_frame->tp.filepath;
168
+ trace->lineno = last_frame->tp.lineno;
169
+ }
170
+
143
171
  static void event_hook(VALUE tpval, void *data) {
144
172
  Rotoscope *config = (Rotoscope *)data;
145
-
146
173
  if (config->tid != gettid()) return;
147
174
  if (in_fork(config)) {
148
175
  rb_tracepoint_disable(config->tracepoint);
@@ -152,39 +179,41 @@ static void event_hook(VALUE tpval, void *data) {
152
179
 
153
180
  rb_trace_arg_t *trace_arg = rb_tracearg_from_tracepoint(tpval);
154
181
  rb_event_flag_t event_flag = rb_tracearg_event_flag(trace_arg);
155
-
156
- rs_callsite_t trace_path;
157
- bool blacklisted;
158
- if (event_flag & EVENT_CALL) {
159
- trace_path = tracearg_path(trace_arg);
160
- blacklisted = rejected_path(trace_path.filepath, config);
161
- } else {
162
- if (rs_stack_empty(&config->stack)) return;
163
- rs_stack_frame_t *call_frame = rs_stack_peek(&config->stack);
164
- trace_path = (rs_callsite_t){
165
- .filepath = call_frame->tp.filepath, .lineno = call_frame->tp.lineno,
166
- };
167
- blacklisted = call_frame->blacklisted;
182
+ rs_callsite_t trace_path = tracearg_path(trace_arg);
183
+ rs_raw_tracepoint_t raw_trace = rs_raw_from_tracepoint(tpval);
184
+
185
+ if (rejected_path(trace_path.filepath, config)) {
186
+ if (METHOD_CALL_P(event_flag)) return;
187
+ // We must ensure the event being blacklisted has correct filepath set,
188
+ // since block-method returns always point one level too high in the stack.
189
+
190
+ // Does this match the last caller?
191
+ rs_stack_frame_t *last_frame = rs_stack_peek(&config->stack);
192
+ if (!rs_raw_tracepoint_cmp(&raw_trace, &last_frame->tp.raw)) return;
193
+ // Are we dealing with a block return in wrong context?
194
+ if (!endof_block_method(trace_arg)) return;
168
195
  }
169
196
 
170
197
  rs_tracepoint_t trace = extract_full_tracevals(trace_arg, &trace_path);
198
+ trace.raw = raw_trace;
199
+
171
200
  if (!strcmp("Rotoscope", StringValueCStr(trace.entity))) return;
172
201
 
173
- if (event_flag & EVENT_CALL) {
174
- rs_stack_push(&config->stack, trace, blacklisted);
202
+ rs_stack_frame_t frame;
203
+ if (METHOD_CALL_P(event_flag)) {
204
+ frame = rs_stack_push(&config->stack, trace);
175
205
  } else {
206
+ if (invalid_stack_return(&config->stack, &trace)) return;
207
+ if (endof_block_method(trace_arg)) {
208
+ copy_filepath_from_caller(&config->stack, &trace);
209
+ }
176
210
  rs_stack_pop(&config->stack);
177
211
  }
178
212
 
179
- if (blacklisted) return;
180
-
181
- if (config->flatten_output) {
182
- if (event_flag & EVENT_CALL) {
183
- log_trace_event_with_caller(config->log, rs_stack_peek(&config->stack),
184
- &config->call_memo);
185
- }
186
- } else {
187
- log_trace_event(config->log, &trace);
213
+ if (!config->flatten_output) {
214
+ return log_trace_event(config->log, trace);
215
+ } else if (METHOD_CALL_P(event_flag)) {
216
+ log_trace_event_with_caller(config->log, &config->call_memo, frame);
188
217
  }
189
218
  }
190
219
 
@@ -313,7 +342,6 @@ VALUE rotoscope_stop_trace(VALUE self) {
313
342
  if (rb_tracepoint_enabled_p(config->tracepoint)) {
314
343
  rb_tracepoint_disable(config->tracepoint);
315
344
  config->state = RS_OPEN;
316
- rs_stack_reset(&config->stack, STACK_CAPACITY);
317
345
  }
318
346
 
319
347
  return Qnil;
@@ -368,9 +396,13 @@ VALUE rotoscope_state(VALUE self) {
368
396
  }
369
397
 
370
398
  void Init_rotoscope(void) {
399
+ VALUE cRubyVm = rb_const_get(rb_cObject, rb_intern("RubyVM"));
400
+ cInstructionSeq = rb_const_get(cRubyVm, rb_intern("InstructionSequence"));
371
401
  cTracePoint = rb_const_get(rb_cObject, rb_intern("TracePoint"));
372
402
 
373
403
  id_initialize = rb_intern("initialize");
404
+ id_of = rb_intern("of");
405
+ id_label = rb_intern("label");
374
406
 
375
407
  cRotoscope = rb_define_class("Rotoscope", rb_cObject);
376
408
  rb_define_alloc_func(cRotoscope, rs_alloc);
@@ -7,10 +7,14 @@
7
7
 
8
8
  #define EVENT_CALL (RUBY_EVENT_CALL | RUBY_EVENT_C_CALL)
9
9
  #define EVENT_RETURN (RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN)
10
+ #define METHOD_CALL_P(event) event &EVENT_CALL
11
+ #define METHOD_RETURN_P(event) event &EVENT_RETURN
10
12
 
11
13
  #define CLASS_METHOD "class"
12
14
  #define INSTANCE_METHOD "instance"
13
15
 
16
+ #define VM_BLOCK_PREFIX "block "
17
+
14
18
  #define STACK_CAPACITY 500
15
19
  #define LOG_BUFFER_SIZE 1000
16
20
 
@@ -18,24 +22,24 @@
18
22
  #define _RS_COMMON_CSV_HEADER "entity,method_name,method_level,filepath,lineno"
19
23
  #define _RS_COMMON_CSV_FORMAT "\"%s\",\"%s\",%s,\"%s\",%d"
20
24
  #define _RS_COMMON_CSV_VALUES(trace) \
21
- StringValueCStr((trace)->entity), \
22
- StringValueCStr((trace)->method_name), \
23
- (trace)->method_level, \
24
- StringValueCStr((trace)->filepath), \
25
- (trace)->lineno
25
+ StringValueCStr(trace.entity), \
26
+ StringValueCStr(trace.method_name), \
27
+ trace.method_level, \
28
+ StringValueCStr(trace.filepath), \
29
+ trace.lineno
26
30
 
27
31
  #define RS_CSV_HEADER "event," _RS_COMMON_CSV_HEADER
28
32
  #define RS_CSV_FORMAT "%s," _RS_COMMON_CSV_FORMAT
29
- #define RS_CSV_VALUES(trace) trace->event, _RS_COMMON_CSV_VALUES(trace)
33
+ #define RS_CSV_VALUES(trace) trace.event, _RS_COMMON_CSV_VALUES(trace)
30
34
 
31
35
  #define RS_FLATTENED_CSV_HEADER \
32
36
  _RS_COMMON_CSV_HEADER ",caller_entity,caller_method_name,caller_method_level"
33
37
  #define RS_FLATTENED_CSV_FORMAT _RS_COMMON_CSV_FORMAT ",\"%s\",\"%s\",%s"
34
- #define RS_FLATTENED_CSV_VALUES(trace, caller_trace) \
35
- _RS_COMMON_CSV_VALUES(trace), \
36
- StringValueCStr((caller_trace)->entity), \
37
- StringValueCStr((caller_trace)->method_name), \
38
- (caller_trace)->method_level
38
+ #define RS_FLATTENED_CSV_VALUES(frame) \
39
+ _RS_COMMON_CSV_VALUES(frame.tp), \
40
+ StringValueCStr(frame.caller->tp.entity), \
41
+ StringValueCStr(frame.caller->tp.method_name), \
42
+ frame.caller->tp.method_level
39
43
  // clang-format on
40
44
 
41
45
  typedef enum {
@@ -14,7 +14,7 @@ static void insert_root_node(rs_stack_t *stack) {
14
14
  .method_name = rb_unknown_str,
15
15
  .method_level = UNKNOWN_STR,
16
16
  .lineno = 0};
17
- rs_stack_push(stack, root_trace, false);
17
+ rs_stack_push(stack, root_trace);
18
18
  }
19
19
 
20
20
  static void resize_buffer(rs_stack_t *stack) {
@@ -31,16 +31,15 @@ bool rs_stack_full(rs_stack_t *stack) {
31
31
 
32
32
  bool rs_stack_empty(rs_stack_t *stack) { return stack->top < 0; }
33
33
 
34
- rs_stack_frame_t rs_stack_push(rs_stack_t *stack, rs_tracepoint_t trace,
35
- bool blacklisted) {
34
+ rs_stack_frame_t rs_stack_push(rs_stack_t *stack, rs_tracepoint_t trace) {
36
35
  if (rs_stack_full(stack)) {
37
36
  resize_buffer(stack);
38
37
  }
39
38
 
40
39
  rs_stack_frame_t *caller =
41
40
  rs_stack_empty(stack) ? NULL : rs_stack_peek(stack);
42
- rs_stack_frame_t new_frame = (rs_stack_frame_t){
43
- .tp = trace, .caller = caller, .blacklisted = blacklisted};
41
+ rs_stack_frame_t new_frame =
42
+ (rs_stack_frame_t){.tp = trace, .caller = caller};
44
43
 
45
44
  stack->contents[++stack->top] = new_frame;
46
45
  return new_frame;
@@ -1,5 +1,6 @@
1
1
  #ifndef _INC_ROTOSCOPE_STACK_H_
2
2
  #define _INC_ROTOSCOPE_STACK_H_
3
+
3
4
  #include <stdbool.h>
4
5
  #include "tracepoint.h"
5
6
 
@@ -7,7 +8,6 @@
7
8
 
8
9
  typedef struct rs_stack_frame_t {
9
10
  struct rs_tracepoint_t tp;
10
- bool blacklisted;
11
11
  struct rs_stack_frame_t *caller;
12
12
  } rs_stack_frame_t;
13
13
 
@@ -20,7 +20,7 @@ typedef struct {
20
20
  void rs_stack_init(rs_stack_t *stack, unsigned int capacity);
21
21
  void rs_stack_reset(rs_stack_t *stack, unsigned int capacity);
22
22
  void rs_stack_free(rs_stack_t *stack);
23
- rs_stack_frame_t rs_stack_push(rs_stack_t *stack, rs_tracepoint_t trace, bool backlisted);
23
+ rs_stack_frame_t rs_stack_push(rs_stack_t *stack, rs_tracepoint_t trace);
24
24
  bool rs_stack_empty(rs_stack_t *stack);
25
25
  bool rs_stack_full(rs_stack_t *stack);
26
26
  rs_stack_frame_t rs_stack_pop(rs_stack_t *stack);
@@ -1,8 +1,26 @@
1
1
  #include "tracepoint.h"
2
2
  #include "ruby.h"
3
3
 
4
+ rs_raw_tracepoint_t rs_raw_from_tracepoint(VALUE tracepoint) {
5
+ rb_trace_arg_t *trace_arg = rb_tracearg_from_tracepoint(tracepoint);
6
+ return (rs_raw_tracepoint_t){
7
+ .self = rb_tracearg_self(trace_arg),
8
+ .method_id = rb_tracearg_method_id(trace_arg),
9
+ };
10
+ }
11
+
12
+ void rs_raw_tracepoint_mark(rs_raw_tracepoint_t *tracepoint) {
13
+ rb_gc_mark(tracepoint->method_id);
14
+ rb_gc_mark(tracepoint->self);
15
+ }
16
+
4
17
  void rs_tracepoint_mark(rs_tracepoint_t *tracepoint) {
5
18
  rb_gc_mark(tracepoint->entity);
6
19
  rb_gc_mark(tracepoint->filepath);
7
20
  rb_gc_mark(tracepoint->method_name);
21
+ rs_raw_tracepoint_mark(&tracepoint->raw);
22
+ }
23
+
24
+ bool rs_raw_tracepoint_cmp(rs_raw_tracepoint_t *tp1, rs_raw_tracepoint_t *tp2) {
25
+ return (tp1->method_id == tp2->method_id) && (tp1->self == tp2->self);
8
26
  }
@@ -1,7 +1,18 @@
1
1
  #ifndef _INC_ROTOSCOPE_TRACEPOINT_H_
2
2
  #define _INC_ROTOSCOPE_TRACEPOINT_H_
3
3
 
4
- #include "ruby.h"
4
+ #include <ruby.h>
5
+ #include <ruby/debug.h>
6
+ #include <stdbool.h>
7
+
8
+ typedef struct rs_raw_tracepoint_t {
9
+ VALUE method_id;
10
+ VALUE self;
11
+ } rs_raw_tracepoint_t;
12
+
13
+ rs_raw_tracepoint_t rs_raw_from_tracepoint(VALUE tracepoint);
14
+ void rs_raw_tracepoint_mark(rs_raw_tracepoint_t *tracepoint);
15
+ bool rs_raw_tracepoint_cmp(rs_raw_tracepoint_t *tp1, rs_raw_tracepoint_t *tp2);
5
16
 
6
17
  typedef struct rs_tracepoint_t {
7
18
  const char *event;
@@ -10,6 +21,7 @@ typedef struct rs_tracepoint_t {
10
21
  VALUE method_name;
11
22
  const char *method_level;
12
23
  unsigned int lineno;
24
+ rs_raw_tracepoint_t raw;
13
25
  } rs_tracepoint_t;
14
26
 
15
27
  void rs_tracepoint_mark(rs_tracepoint_t *tracepoint);
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  class Rotoscope
3
- VERSION = "0.3.0.pre.1"
3
+ VERSION = "0.3.0.pre.2"
4
4
  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.1
4
+ version: 0.3.0.pre.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jahfer Husain
@@ -120,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
120
  version: 1.3.1
121
121
  requirements: []
122
122
  rubyforge_project:
123
- rubygems_version: 2.6.13
123
+ rubygems_version: 2.6.14
124
124
  signing_key:
125
125
  specification_version: 4
126
126
  summary: Tracing Ruby