rotoscope 0.3.0.pre.6 → 0.3.0.pre.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/rotoscope/extconf.rb +0 -3
- data/ext/rotoscope/rotoscope.c +95 -120
- data/ext/rotoscope/rotoscope.h +6 -18
- data/lib/rotoscope.rb +34 -35
- data/lib/rotoscope/version.rb +1 -1
- metadata +1 -4
- data/ext/rotoscope/strmemo.c +0 -33
- data/ext/rotoscope/strmemo.h +0 -14
- data/lib/uthash/uthash.h +0 -1107
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc57c07b922488935b47e583d422a36d9fa32096
|
4
|
+
data.tar.gz: cd70207ab9f61bcd490506a5defc6c293dbec11d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c512251290df7ee37e72e0f130ca879da887f3ecdd206b60e9e4a361e422bf68daf6b6c42acbb1c20a40efaa4c7166334fc2e21d0df2e3dc1f9c722267b31c0
|
7
|
+
data.tar.gz: a5e1d3bdd7ae63147097154630aae6bb9c4153fa110be4fc1873cbce38f7d684a3e0f87cc1c80f4dfd811c04e21d21c8dcc5adad7de4afc91a977dc266ac0d41
|
data/ext/rotoscope/extconf.rb
CHANGED
@@ -4,7 +4,4 @@ require "mkmf"
|
|
4
4
|
$CFLAGS << ' -std=c99 -Wall -Werror -Wno-declaration-after-statement'
|
5
5
|
$defs << "-D_POSIX_SOURCE"
|
6
6
|
|
7
|
-
uthash_path = File.expand_path('../../../lib/uthash', __FILE__)
|
8
|
-
find_header('uthash.h', uthash_path) || raise
|
9
|
-
|
10
7
|
create_makefile('rotoscope/rotoscope')
|
data/ext/rotoscope/rotoscope.c
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
#include <ruby.h>
|
3
3
|
#include <ruby/debug.h>
|
4
4
|
#include <ruby/intern.h>
|
5
|
+
#include <ruby/io.h>
|
5
6
|
#include <ruby/version.h>
|
6
7
|
#include <stdbool.h>
|
7
8
|
#include <stdio.h>
|
@@ -10,21 +11,16 @@
|
|
10
11
|
#include "callsite.h"
|
11
12
|
#include "rotoscope.h"
|
12
13
|
#include "stack.h"
|
13
|
-
#include "strmemo.h"
|
14
14
|
#include "tracepoint.h"
|
15
15
|
|
16
16
|
VALUE cRotoscope, cTracePoint;
|
17
|
-
ID id_initialize, id_gsub;
|
18
|
-
VALUE str_quote, str_escaped_quote;
|
17
|
+
ID id_initialize, id_gsub, id_close;
|
18
|
+
VALUE str_quote, str_escaped_quote, str_header;
|
19
19
|
|
20
20
|
static unsigned long gettid() {
|
21
21
|
return NUM2ULONG(rb_obj_id(rb_thread_current()));
|
22
22
|
}
|
23
23
|
|
24
|
-
static int write_csv_header(FILE *log, const char *header) {
|
25
|
-
return fprintf(log, "%s\n", header);
|
26
|
-
}
|
27
|
-
|
28
24
|
static const char *evflag2name(rb_event_flag_t evflag) {
|
29
25
|
switch (evflag) {
|
30
26
|
case RUBY_EVENT_CALL:
|
@@ -120,6 +116,17 @@ static rs_tracepoint_t extract_full_tracevals(rb_trace_arg_t *trace_arg,
|
|
120
116
|
|
121
117
|
static bool in_fork(Rotoscope *config) { return config->pid != getpid(); }
|
122
118
|
|
119
|
+
// The GC sweep step will turn objects with finalizers (e.g. rs_dealloc)
|
120
|
+
// to zombie objects until their finalizer is run. In this state, any
|
121
|
+
// ruby objects in the Rotoscope struct may have already been collected
|
122
|
+
// so they can't safely be used. If tracing isn't stopped before the
|
123
|
+
// Rotoscope object has been garbage collected, then we still may receive
|
124
|
+
// trace events for method calls in finalizers that run before the one
|
125
|
+
// for the Rotoscope object.
|
126
|
+
bool rotoscope_marked_for_garbage_collection(Rotoscope *config) {
|
127
|
+
return RB_BUILTIN_TYPE(config->self) == RUBY_T_ZOMBIE;
|
128
|
+
}
|
129
|
+
|
123
130
|
VALUE escape_csv_string(VALUE string) {
|
124
131
|
if (!memchr(RSTRING_PTR(string), '"', RSTRING_LEN(string))) {
|
125
132
|
return string;
|
@@ -127,36 +134,55 @@ VALUE escape_csv_string(VALUE string) {
|
|
127
134
|
return rb_funcall(string, id_gsub, 2, str_quote, str_escaped_quote);
|
128
135
|
}
|
129
136
|
|
130
|
-
static void
|
131
|
-
VALUE escaped_method_name = escape_csv_string(trace->method_name);
|
132
|
-
fprintf(stream, RS_CSV_FORMAT "\n",
|
133
|
-
RS_CSV_VALUES(trace, escaped_method_name));
|
134
|
-
RB_GC_GUARD(escaped_method_name);
|
135
|
-
}
|
136
|
-
|
137
|
-
unsigned char output_buffer[LOG_BUFFER_SIZE];
|
138
|
-
static void log_trace_event_with_caller(FILE *stream,
|
137
|
+
static void log_trace_event_with_caller(VALUE output_buffer, VALUE io,
|
139
138
|
rs_stack_frame_t *stack_frame,
|
140
|
-
rs_stack_frame_t *caller_frame
|
141
|
-
rs_strmemo_t **call_memo) {
|
139
|
+
rs_stack_frame_t *caller_frame) {
|
142
140
|
VALUE escaped_method_name = escape_csv_string(stack_frame->tp.method_name);
|
143
141
|
VALUE escaped_caller_method_name =
|
144
142
|
escape_csv_string(caller_frame->tp.method_name);
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
+
|
149
159
|
RB_GC_GUARD(escaped_method_name);
|
150
160
|
RB_GC_GUARD(escaped_caller_method_name);
|
151
161
|
|
152
|
-
|
153
|
-
|
162
|
+
rb_io_write(io, output_buffer);
|
163
|
+
}
|
164
|
+
|
165
|
+
static void stop_tracing_on_cleanup(Rotoscope *config) {
|
166
|
+
if (config->state == RS_TRACING) {
|
167
|
+
// During process cleanup, event hooks are removed and tracepoint may have
|
168
|
+
// already have been GCed, so we need a sanity check before disabling the
|
169
|
+
// tracepoint.
|
170
|
+
if (RB_TYPE_P(config->tracepoint, T_DATA) &&
|
171
|
+
CLASS_OF(config->tracepoint) == cTracePoint) {
|
172
|
+
rb_tracepoint_disable(config->tracepoint);
|
173
|
+
}
|
174
|
+
config->state = RS_OPEN;
|
154
175
|
}
|
155
176
|
}
|
156
177
|
|
157
178
|
static void event_hook(VALUE tpval, void *data) {
|
158
179
|
Rotoscope *config = (Rotoscope *)data;
|
159
180
|
|
181
|
+
if (rotoscope_marked_for_garbage_collection(config)) {
|
182
|
+
stop_tracing_on_cleanup(config);
|
183
|
+
return;
|
184
|
+
}
|
185
|
+
|
160
186
|
if (config->tid != gettid()) return;
|
161
187
|
if (in_fork(config)) {
|
162
188
|
rb_tracepoint_disable(config->tracepoint);
|
@@ -165,77 +191,45 @@ static void event_hook(VALUE tpval, void *data) {
|
|
165
191
|
}
|
166
192
|
|
167
193
|
rb_trace_arg_t *trace_arg = rb_tracearg_from_tracepoint(tpval);
|
168
|
-
rb_event_flag_t event_flag = rb_tracearg_event_flag(trace_arg);
|
169
194
|
|
170
|
-
|
171
|
-
|
172
|
-
if (event_flag & EVENT_CALL) {
|
173
|
-
trace_path = tracearg_path(trace_arg);
|
174
|
-
blacklisted = rejected_path(trace_path.filepath, config);
|
175
|
-
} else {
|
176
|
-
if (rs_stack_empty(&config->stack)) return;
|
177
|
-
rs_stack_frame_t *call_frame = rs_stack_peek(&config->stack);
|
178
|
-
trace_path = (rs_callsite_t){
|
179
|
-
.filepath = call_frame->tp.filepath, .lineno = call_frame->tp.lineno,
|
180
|
-
};
|
181
|
-
blacklisted = call_frame->blacklisted;
|
195
|
+
if (rb_tracearg_defined_class(trace_arg) == cRotoscope) {
|
196
|
+
return;
|
182
197
|
}
|
183
198
|
|
184
|
-
|
185
|
-
if (!strcmp("Rotoscope", StringValueCStr(trace.entity))) return;
|
186
|
-
|
187
|
-
if (event_flag & EVENT_CALL) {
|
188
|
-
rs_stack_push(&config->stack, trace, blacklisted);
|
189
|
-
} else {
|
190
|
-
rs_stack_pop(&config->stack);
|
191
|
-
}
|
192
|
-
if (blacklisted) return;
|
199
|
+
rb_event_flag_t event_flag = rb_tracearg_event_flag(trace_arg);
|
193
200
|
|
194
|
-
if (
|
195
|
-
if (
|
196
|
-
|
197
|
-
rs_stack_frame_t *caller_frame =
|
198
|
-
rs_stack_below(&config->stack, stack_frame);
|
199
|
-
log_trace_event_with_caller(config->log, stack_frame, caller_frame,
|
200
|
-
&config->call_memo);
|
201
|
+
if (event_flag & EVENT_RETURN) {
|
202
|
+
if (!rs_stack_empty(&config->stack)) {
|
203
|
+
rs_stack_pop(&config->stack);
|
201
204
|
}
|
202
|
-
|
203
|
-
log_trace_event(config->log, &trace);
|
205
|
+
return;
|
204
206
|
}
|
205
|
-
}
|
206
207
|
|
207
|
-
|
208
|
-
|
209
|
-
// Stop tracing so the event hook isn't called with a NULL log
|
210
|
-
if (config->state == RS_TRACING) {
|
211
|
-
// During process cleanup, event hooks are removed and tracepoint may have
|
212
|
-
// already have been GCed, so we need a sanity check before disabling the
|
213
|
-
// tracepoint.
|
214
|
-
if (RB_TYPE_P(config->tracepoint, T_DATA) &&
|
215
|
-
CLASS_OF(config->tracepoint) == cTracePoint) {
|
216
|
-
rb_tracepoint_disable(config->tracepoint);
|
217
|
-
}
|
218
|
-
}
|
208
|
+
rs_callsite_t trace_path = tracearg_path(trace_arg);
|
209
|
+
bool blacklisted = rejected_path(trace_path.filepath, config);
|
219
210
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
211
|
+
rs_tracepoint_t trace = extract_full_tracevals(trace_arg, &trace_path);
|
212
|
+
|
213
|
+
rs_stack_push(&config->stack, trace, blacklisted);
|
214
|
+
|
215
|
+
if (blacklisted) return;
|
216
|
+
|
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);
|
227
221
|
}
|
228
222
|
|
229
223
|
static void rs_gc_mark(Rotoscope *config) {
|
230
|
-
rb_gc_mark(config->
|
224
|
+
rb_gc_mark(config->log);
|
231
225
|
rb_gc_mark(config->tracepoint);
|
226
|
+
rb_gc_mark(config->output_buffer);
|
232
227
|
rs_stack_mark(&config->stack);
|
233
228
|
}
|
234
229
|
|
235
230
|
void rs_dealloc(Rotoscope *config) {
|
236
|
-
|
231
|
+
stop_tracing_on_cleanup(config);
|
237
232
|
rs_stack_free(&config->stack);
|
238
|
-
rs_strmemo_free(config->call_memo);
|
239
233
|
xfree(config->blacklist);
|
240
234
|
xfree(config);
|
241
235
|
}
|
@@ -244,11 +238,13 @@ static VALUE rs_alloc(VALUE klass) {
|
|
244
238
|
Rotoscope *config;
|
245
239
|
VALUE self =
|
246
240
|
Data_Make_Struct(klass, Rotoscope, rs_gc_mark, rs_dealloc, config);
|
247
|
-
config->
|
241
|
+
config->self = self;
|
242
|
+
config->log = Qnil;
|
248
243
|
config->tracepoint = rb_tracepoint_new(Qnil, EVENT_CALL | EVENT_RETURN,
|
249
244
|
event_hook, (void *)config);
|
250
245
|
config->pid = getpid();
|
251
246
|
config->tid = gettid();
|
247
|
+
config->output_buffer = Qnil;
|
252
248
|
return self;
|
253
249
|
}
|
254
250
|
|
@@ -287,33 +283,21 @@ void copy_blacklist(Rotoscope *config, VALUE blacklist) {
|
|
287
283
|
|
288
284
|
VALUE initialize(int argc, VALUE *argv, VALUE self) {
|
289
285
|
Rotoscope *config = get_config(self);
|
290
|
-
VALUE
|
286
|
+
VALUE output, blacklist;
|
291
287
|
|
292
|
-
rb_scan_args(argc, argv, "
|
293
|
-
Check_Type(output_path, T_STRING);
|
288
|
+
rb_scan_args(argc, argv, "11", &output, &blacklist);
|
294
289
|
|
295
290
|
if (!NIL_P(blacklist)) {
|
296
291
|
copy_blacklist(config, blacklist);
|
297
292
|
}
|
298
293
|
|
299
|
-
config->
|
300
|
-
config->log_path = output_path;
|
301
|
-
config->log = fopen(StringValueCStr(config->log_path), "w");
|
294
|
+
config->log = output;
|
302
295
|
|
303
|
-
|
304
|
-
fprintf(stderr, "\nERROR: Failed to open file handle at %s (%s)\n",
|
305
|
-
StringValueCStr(config->log_path), strerror(errno));
|
306
|
-
exit(1);
|
307
|
-
}
|
308
|
-
|
309
|
-
if (config->flatten_output)
|
310
|
-
write_csv_header(config->log, RS_FLATTENED_CSV_HEADER);
|
311
|
-
else
|
312
|
-
write_csv_header(config->log, RS_CSV_HEADER);
|
296
|
+
rb_io_write(config->log, str_header);
|
313
297
|
|
314
298
|
rs_stack_init(&config->stack, STACK_CAPACITY);
|
315
|
-
config->call_memo = NULL;
|
316
299
|
config->state = RS_OPEN;
|
300
|
+
config->output_buffer = rb_str_buf_new(LOG_BUFFER_SIZE);
|
317
301
|
return self;
|
318
302
|
}
|
319
303
|
|
@@ -335,36 +319,25 @@ VALUE rotoscope_stop_trace(VALUE self) {
|
|
335
319
|
return Qnil;
|
336
320
|
}
|
337
321
|
|
338
|
-
VALUE rotoscope_log_path(VALUE self) {
|
339
|
-
Rotoscope *config = get_config(self);
|
340
|
-
return config->log_path;
|
341
|
-
}
|
342
|
-
|
343
|
-
VALUE rotoscope_mark(int argc, VALUE *argv, VALUE self) {
|
344
|
-
VALUE str;
|
345
|
-
rb_scan_args(argc, argv, "01", &str);
|
346
|
-
|
347
|
-
if (NIL_P(str)) str = rb_str_new2("");
|
348
|
-
Check_Type(str, T_STRING);
|
349
|
-
|
350
|
-
Rotoscope *config = get_config(self);
|
351
|
-
if (config->log != NULL && !in_fork(config)) {
|
352
|
-
rs_strmemo_free(config->call_memo);
|
353
|
-
fprintf(config->log, "--- %s\n", StringValueCStr(str));
|
354
|
-
}
|
355
|
-
return Qnil;
|
356
|
-
}
|
357
|
-
|
358
322
|
VALUE rotoscope_close(VALUE self) {
|
359
323
|
Rotoscope *config = get_config(self);
|
360
324
|
if (config->state == RS_CLOSED) {
|
361
325
|
return Qtrue;
|
362
326
|
}
|
363
|
-
|
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
|
+
}
|
364
332
|
config->state = RS_CLOSED;
|
365
333
|
return Qtrue;
|
366
334
|
}
|
367
335
|
|
336
|
+
VALUE rotoscope_io(VALUE self) {
|
337
|
+
Rotoscope *config = get_config(self);
|
338
|
+
return config->log;
|
339
|
+
}
|
340
|
+
|
368
341
|
VALUE rotoscope_trace(VALUE self) {
|
369
342
|
rotoscope_start_trace(self);
|
370
343
|
return rb_ensure(rb_yield, Qundef, rotoscope_stop_trace, self);
|
@@ -387,24 +360,26 @@ void Init_rotoscope(void) {
|
|
387
360
|
|
388
361
|
id_initialize = rb_intern("initialize");
|
389
362
|
id_gsub = rb_intern("gsub");
|
363
|
+
id_close = rb_intern("close");
|
390
364
|
|
391
365
|
str_quote = rb_str_new_literal("\"");
|
392
366
|
rb_global_variable(&str_quote);
|
393
367
|
str_escaped_quote = rb_str_new_literal("\"\"");
|
394
368
|
rb_global_variable(&str_escaped_quote);
|
395
369
|
|
370
|
+
str_header = rb_str_new_literal(RS_CSV_HEADER "\n");
|
371
|
+
rb_global_variable(&str_header);
|
372
|
+
|
396
373
|
cRotoscope = rb_define_class("Rotoscope", rb_cObject);
|
397
374
|
rb_define_alloc_func(cRotoscope, rs_alloc);
|
398
375
|
rb_define_method(cRotoscope, "initialize", initialize, -1);
|
399
376
|
rb_define_method(cRotoscope, "trace", (VALUE(*)(ANYARGS))rotoscope_trace, 0);
|
400
|
-
rb_define_method(cRotoscope, "mark", (VALUE(*)(ANYARGS))rotoscope_mark, -1);
|
401
377
|
rb_define_method(cRotoscope, "close", (VALUE(*)(ANYARGS))rotoscope_close, 0);
|
378
|
+
rb_define_method(cRotoscope, "io", rotoscope_io, 0);
|
402
379
|
rb_define_method(cRotoscope, "start_trace",
|
403
380
|
(VALUE(*)(ANYARGS))rotoscope_start_trace, 0);
|
404
381
|
rb_define_method(cRotoscope, "stop_trace",
|
405
382
|
(VALUE(*)(ANYARGS))rotoscope_stop_trace, 0);
|
406
|
-
rb_define_method(cRotoscope, "log_path",
|
407
|
-
(VALUE(*)(ANYARGS))rotoscope_log_path, 0);
|
408
383
|
rb_define_method(cRotoscope, "state", (VALUE(*)(ANYARGS))rotoscope_state, 0);
|
409
384
|
|
410
385
|
init_callsite();
|
data/ext/rotoscope/rotoscope.h
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
|
4
4
|
#include <unistd.h>
|
5
5
|
#include "stack.h"
|
6
|
-
#include "strmemo.h"
|
7
6
|
|
8
7
|
#define EVENT_CALL (RUBY_EVENT_CALL | RUBY_EVENT_C_CALL)
|
9
8
|
#define EVENT_RETURN (RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN)
|
@@ -16,20 +15,10 @@
|
|
16
15
|
|
17
16
|
// clang-format off
|
18
17
|
|
19
|
-
#define RS_CSV_HEADER
|
20
|
-
#define RS_CSV_FORMAT "%s,\"%s\",\"%s\",%d,\"%s\",%s"
|
21
|
-
#define RS_CSV_VALUES(trace, method_name) \
|
22
|
-
trace->event, \
|
23
|
-
StringValueCStr((trace)->entity), \
|
24
|
-
StringValueCStr((trace)->filepath), \
|
25
|
-
(trace)->lineno, \
|
26
|
-
StringValueCStr(method_name), \
|
27
|
-
(trace)->method_level
|
28
|
-
|
29
|
-
#define RS_FLATTENED_CSV_HEADER \
|
18
|
+
#define RS_CSV_HEADER \
|
30
19
|
"entity,caller_entity,filepath,lineno,method_name,method_level,caller_method_name,caller_method_level"
|
31
|
-
#define
|
32
|
-
#define
|
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) \
|
33
22
|
StringValueCStr((trace)->entity), \
|
34
23
|
StringValueCStr((caller_trace)->entity), \
|
35
24
|
StringValueCStr((trace)->filepath), \
|
@@ -48,17 +37,16 @@ typedef enum {
|
|
48
37
|
} rs_state;
|
49
38
|
|
50
39
|
typedef struct {
|
51
|
-
|
52
|
-
VALUE
|
40
|
+
VALUE self;
|
41
|
+
VALUE log;
|
53
42
|
VALUE tracepoint;
|
54
43
|
const char **blacklist;
|
55
44
|
unsigned long blacklist_size;
|
56
|
-
bool flatten_output;
|
57
45
|
pid_t pid;
|
58
46
|
unsigned long tid;
|
59
47
|
rs_state state;
|
60
48
|
rs_stack_t stack;
|
61
|
-
|
49
|
+
VALUE output_buffer;
|
62
50
|
} Rotoscope;
|
63
51
|
|
64
52
|
typedef struct {
|
data/lib/rotoscope.rb
CHANGED
@@ -1,56 +1,55 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'rotoscope/rotoscope'
|
3
|
-
require 'fileutils'
|
4
|
-
require 'tempfile'
|
5
3
|
require 'csv'
|
6
4
|
|
7
5
|
class Rotoscope
|
8
6
|
class << self
|
9
|
-
def new(
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
event_trace(dest, config, &block)
|
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
|
17
14
|
else
|
18
|
-
|
15
|
+
super(output, blacklist)
|
19
16
|
end
|
20
17
|
end
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
yield temp_file
|
19
|
+
def trace(dest, blacklist: [])
|
20
|
+
rs = new(dest, blacklist: blacklist)
|
21
|
+
rs.trace { yield rs }
|
22
|
+
rs
|
27
23
|
ensure
|
28
|
-
|
24
|
+
rs.close if rs && dest.is_a?(String)
|
29
25
|
end
|
30
26
|
|
31
|
-
|
32
|
-
with_temp_file("rotoscope_output") do |temp_file|
|
33
|
-
rs = event_trace(temp_file.path, config, &block)
|
34
|
-
yield rs
|
35
|
-
rs
|
36
|
-
end
|
37
|
-
end
|
27
|
+
private
|
38
28
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
44
36
|
end
|
37
|
+
ObjectSpace.define_finalizer(io, finalizer)
|
45
38
|
end
|
39
|
+
end
|
46
40
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
53
48
|
end
|
49
|
+
io.write("--- ")
|
50
|
+
io.puts(message)
|
51
|
+
ensure
|
52
|
+
start_trace if state == :tracing
|
54
53
|
end
|
55
54
|
|
56
55
|
def closed?
|