rotoscope 0.3.0.pre.6 → 0.3.0.pre.7
Sign up to get free protection for your applications and to get access to all the features.
- 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?
|