google-cloud-debugger 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +18 -0
  3. data/AUTHENTICATION.md +178 -0
  4. data/CHANGELOG.md +233 -0
  5. data/CODE_OF_CONDUCT.md +40 -0
  6. data/CONTRIBUTING.md +188 -0
  7. data/INSTRUMENTATION.md +115 -0
  8. data/LICENSE +201 -0
  9. data/LOGGING.md +32 -0
  10. data/OVERVIEW.md +266 -0
  11. data/TROUBLESHOOTING.md +31 -0
  12. data/ext/google/cloud/debugger/debugger_c/debugger.c +31 -0
  13. data/ext/google/cloud/debugger/debugger_c/debugger.h +26 -0
  14. data/ext/google/cloud/debugger/debugger_c/evaluator.c +115 -0
  15. data/ext/google/cloud/debugger/debugger_c/evaluator.h +25 -0
  16. data/ext/google/cloud/debugger/debugger_c/extconf.rb +22 -0
  17. data/ext/google/cloud/debugger/debugger_c/tracer.c +542 -0
  18. data/ext/google/cloud/debugger/debugger_c/tracer.h +25 -0
  19. data/lib/google-cloud-debugger.rb +181 -0
  20. data/lib/google/cloud/debugger.rb +259 -0
  21. data/lib/google/cloud/debugger/agent.rb +255 -0
  22. data/lib/google/cloud/debugger/backoff.rb +70 -0
  23. data/lib/google/cloud/debugger/breakpoint.rb +443 -0
  24. data/lib/google/cloud/debugger/breakpoint/evaluator.rb +1099 -0
  25. data/lib/google/cloud/debugger/breakpoint/source_location.rb +74 -0
  26. data/lib/google/cloud/debugger/breakpoint/stack_frame.rb +109 -0
  27. data/lib/google/cloud/debugger/breakpoint/status_message.rb +93 -0
  28. data/lib/google/cloud/debugger/breakpoint/validator.rb +92 -0
  29. data/lib/google/cloud/debugger/breakpoint/variable.rb +595 -0
  30. data/lib/google/cloud/debugger/breakpoint/variable_table.rb +96 -0
  31. data/lib/google/cloud/debugger/breakpoint_manager.rb +311 -0
  32. data/lib/google/cloud/debugger/credentials.rb +50 -0
  33. data/lib/google/cloud/debugger/debuggee.rb +222 -0
  34. data/lib/google/cloud/debugger/debuggee/app_uniquifier_generator.rb +76 -0
  35. data/lib/google/cloud/debugger/logpoint.rb +98 -0
  36. data/lib/google/cloud/debugger/middleware.rb +200 -0
  37. data/lib/google/cloud/debugger/project.rb +110 -0
  38. data/lib/google/cloud/debugger/rails.rb +174 -0
  39. data/lib/google/cloud/debugger/request_quota_manager.rb +95 -0
  40. data/lib/google/cloud/debugger/service.rb +88 -0
  41. data/lib/google/cloud/debugger/snappoint.rb +208 -0
  42. data/lib/google/cloud/debugger/tracer.rb +137 -0
  43. data/lib/google/cloud/debugger/transmitter.rb +199 -0
  44. data/lib/google/cloud/debugger/version.rb +22 -0
  45. metadata +353 -0
@@ -0,0 +1,25 @@
1
+ /*
2
+ Copyright 2017 Google Inc. All rights reserved.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ https://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ #ifndef GOOGLE_CLOUD_RUBY_DEBUGGER_EVALUATOR_H_
18
+ #define GOOGLE_CLOUD_RUBY_DEBUGGER_EVALUATOR_H_
19
+
20
+ #include "ruby/debug.h"
21
+
22
+ void
23
+ Init_evaluator(VALUE mDebugger);
24
+
25
+ #endif // GOOGLE_CLOUD_RUBY_DEBUGGER_TRACER_H_
@@ -0,0 +1,22 @@
1
+ # Copyright 2017 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "mkmf"
17
+
18
+ extension_name = "google/cloud/debugger/debugger_c"
19
+
20
+ dir_config extension_name
21
+
22
+ create_makefile extension_name
@@ -0,0 +1,542 @@
1
+ /*
2
+ Copyright 2017 Google Inc. All rights reserved.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ https://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ #include "ruby/ruby.h"
18
+ #include "ruby/debug.h"
19
+ #include "tracer.h"
20
+
21
+ #define FILE_TRACEPOINT_EVENTS (RUBY_EVENT_CLASS | RUBY_EVENT_CALL | RUBY_EVENT_B_CALL)
22
+ #define RETURN_TRACEPOINT_EVENTS (RUBY_EVENT_END | RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN)
23
+ #define FIBER_TRACEPOINT_EVENT RUBY_EVENT_FIBER_SWITCH
24
+
25
+ /* To prevent unused parameter warnings */
26
+ #define UNUSED(x) (void)(x)
27
+
28
+ /**
29
+ * hash_get_keys_callback
30
+ * Helper callback function for hash_get_keys.
31
+ */
32
+ static int
33
+ hash_get_keys_callback(VALUE key, VALUE val, VALUE key_ary)
34
+ {
35
+ rb_ary_push(key_ary, key);
36
+
37
+ return ST_CONTINUE;
38
+ }
39
+
40
+ /**
41
+ * hash_get_keys
42
+ * Helper function to return an array of all the keys of a given Ruby array
43
+ */
44
+ static VALUE
45
+ hash_get_keys(VALUE hash)
46
+ {
47
+ VALUE key_ary;
48
+
49
+ if(!RB_TYPE_P(hash, T_HASH)) {
50
+ return Qnil;
51
+ }
52
+
53
+ key_ary = rb_ary_new();
54
+
55
+ rb_hash_foreach(hash, hash_get_keys_callback, key_ary);
56
+
57
+ return key_ary;
58
+ }
59
+
60
+ /**
61
+ * match_breakpoints_files
62
+ * Check the Tracer#breakpoints_cache if any breakpoints match the given
63
+ * tracepoint_path. Return 1 if found. Otherwise 0;
64
+ */
65
+ static int
66
+ match_breakpoints_files(VALUE self, VALUE tracepoint_path)
67
+ {
68
+ int i;
69
+ char *c_tracepoint_path;
70
+ VALUE path_breakpoints_hash;
71
+ VALUE breakpoints_paths;
72
+ VALUE *c_breakpoints_paths;
73
+ int breakpoints_paths_len;
74
+
75
+ // Return 0 if the given path is Qnil
76
+ if(!RTEST(tracepoint_path)) {
77
+ return 0;
78
+ }
79
+
80
+ c_tracepoint_path = rb_string_value_cstr(&tracepoint_path);
81
+ path_breakpoints_hash = rb_iv_get(self, "@breakpoints_cache");
82
+ breakpoints_paths = hash_get_keys(path_breakpoints_hash);
83
+ c_breakpoints_paths = RARRAY_PTR(breakpoints_paths);
84
+ breakpoints_paths_len = RARRAY_LEN(breakpoints_paths);
85
+
86
+ for (i = 0; i < breakpoints_paths_len; i++) {
87
+ VALUE breakpoint_path = c_breakpoints_paths[i];
88
+ char *c_breakpoint_path = rb_string_value_cstr(&breakpoint_path);
89
+
90
+ if (strcmp(c_tracepoint_path, c_breakpoint_path) == 0) {
91
+ return 1;
92
+ }
93
+ }
94
+
95
+ return 0;
96
+ }
97
+
98
+ static VALUE
99
+ disable_line_trace_for_thread(VALUE thread);
100
+
101
+ /**
102
+ * match_breakpoints
103
+ * Check the Tracer#breakpoints_cache for any matching breakpoints of given
104
+ * file path and line number.
105
+ *
106
+ * Return a Ruby array of breakpoints found. Qtrue if no match found, but this
107
+ * file contains at least one breakpoint. Qnil if event triggered in a file
108
+ * that doesn't contain any breakpoints.
109
+ */
110
+ static VALUE
111
+ match_breakpoints(VALUE self, const char *c_trace_path, int c_trace_lineno)
112
+ {
113
+ int i, j;
114
+ VALUE path_breakpoints_hash = rb_iv_get(self, "@breakpoints_cache");
115
+ VALUE breakpoints_paths = hash_get_keys(path_breakpoints_hash);
116
+ VALUE *c_breakpoints_paths = RARRAY_PTR(breakpoints_paths);
117
+ int breakpoints_paths_len = RARRAY_LEN(breakpoints_paths);
118
+ VALUE path_match = Qnil;
119
+
120
+ // Check the file paths of @breakpoints_cache
121
+ for (i = 0; i < breakpoints_paths_len; i++) {
122
+ VALUE breakpoint_path = c_breakpoints_paths[i];
123
+ char *c_breakpoint_path = rb_string_value_cstr(&breakpoint_path);
124
+
125
+ // Found matching file path, keep going and check for the line numbers
126
+ if (strcmp(c_trace_path, c_breakpoint_path) == 0) {
127
+ VALUE line_breakpoint_hash = rb_hash_aref(path_breakpoints_hash, breakpoint_path);
128
+ VALUE breakpoints_lines = hash_get_keys(line_breakpoint_hash);
129
+ VALUE *c_breakpoints_lines = RARRAY_PTR(breakpoints_lines);
130
+ int breakpoints_lines_len = RARRAY_LEN(breakpoints_lines);
131
+ path_match = Qtrue;
132
+
133
+ // Found matching breakpoints. Return the cached breakpoints array
134
+ for (j = 0; j < breakpoints_lines_len; j++) {
135
+ VALUE breakpoint_lineno = c_breakpoints_lines[j];
136
+ int c_breakpoint_lineno = NUM2INT(breakpoint_lineno);
137
+
138
+ if (c_trace_lineno == c_breakpoint_lineno) {
139
+ return rb_hash_aref(line_breakpoint_hash, breakpoint_lineno);
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ return path_match;
146
+ }
147
+
148
+ /**
149
+ * line_trace_callback
150
+ * Callback function for thread line event tracing. It checks Tracer#breakpoints_cache
151
+ * for any breakpoints trigger on current line called. Then trigger evaluation
152
+ * procedure if found matching breakpoints. It also skip breakpoints that are
153
+ * already marked completed.
154
+ */
155
+ static void
156
+ line_trace_callback(rb_event_flag_t event, VALUE data, VALUE obj, ID mid, VALUE klass)
157
+ {
158
+ VALUE self = data;
159
+ VALUE trace_path;
160
+ int c_trace_lineno;
161
+ const char *c_trace_path;
162
+ VALUE trace_binding;
163
+ VALUE call_stack_bindings;
164
+ ID callers_id;
165
+ ID breakpoints_hit_id;
166
+ VALUE matching_result;
167
+
168
+ c_trace_path = rb_sourcefile();
169
+ // Ensure C_trace_path is absolute path
170
+ trace_path = rb_str_new_cstr(c_trace_path);
171
+ trace_path = rb_file_expand_path(trace_path, Qnil);
172
+
173
+ if(!RTEST(trace_path)) {
174
+ return;
175
+ }
176
+
177
+ c_trace_path = rb_string_value_cstr(&trace_path);
178
+
179
+ c_trace_lineno = rb_sourceline();
180
+ matching_result = match_breakpoints(self, c_trace_path, c_trace_lineno);
181
+
182
+ CONST_ID(callers_id, "callers");
183
+ CONST_ID(breakpoints_hit_id, "breakpoints_hit");
184
+
185
+ // If matching result isn't an array, it means we're in completely wrong file,
186
+ // or not on the right line. Turn line tracing off if we're in wrong file.
187
+ if (!RB_TYPE_P(matching_result, T_ARRAY)) {
188
+ if (!RTEST(matching_result)) {
189
+ disable_line_trace_for_thread(Qnil);
190
+ }
191
+ return;
192
+ }
193
+
194
+ trace_binding = rb_binding_new();
195
+ call_stack_bindings = rb_funcall(trace_binding, callers_id, 0);
196
+
197
+ rb_funcall(self, breakpoints_hit_id, 2, matching_result, call_stack_bindings);
198
+
199
+ return;
200
+ }
201
+
202
+ /**
203
+ * disable_line_trace_for_thread
204
+ * Turn off line event trace hook for a given thread. If no thread is given, it
205
+ * turns off line event trace hook in current thread. It only takes action if
206
+ * the thread has a thread variable "gcloud_line_trace_set" that's true.
207
+ */
208
+ static VALUE
209
+ disable_line_trace_for_thread(VALUE thread)
210
+ {
211
+ VALUE line_trace_set;
212
+ ID line_trace_thread_id;
213
+
214
+ CONST_ID(line_trace_thread_id, "gcloud_line_trace_set");
215
+
216
+ if (!RTEST(thread)) {
217
+ thread = rb_thread_current();
218
+ }
219
+ line_trace_set = rb_ivar_get(thread, line_trace_thread_id);
220
+
221
+ if (RTEST(line_trace_set)) {
222
+ rb_thread_remove_event_hook(thread, line_trace_callback);
223
+ rb_ivar_set(thread, line_trace_thread_id, Qfalse);
224
+ }
225
+
226
+ return Qnil;
227
+ }
228
+
229
+ /**
230
+ * enable_line_trace_for_thread
231
+ * Turn on line event trace for current thread. Also set a flag
232
+ * "gcloud_line_trace_set" to Qtrue in current thread's thread variable.
233
+ */
234
+ static VALUE
235
+ enable_line_trace_for_thread(VALUE self)
236
+ {
237
+ VALUE current_thread;
238
+ VALUE line_trace_set;
239
+ ID line_trace_thread_id;
240
+
241
+ CONST_ID(line_trace_thread_id, "gcloud_line_trace_set");
242
+
243
+ current_thread = rb_thread_current();
244
+ line_trace_set = rb_ivar_get(current_thread, line_trace_thread_id);
245
+
246
+ if (!RTEST(line_trace_set)) {
247
+ rb_thread_add_event_hook(current_thread, line_trace_callback, RUBY_EVENT_LINE, self);
248
+ rb_ivar_set(current_thread, line_trace_thread_id, Qtrue);
249
+ }
250
+
251
+ return Qnil;
252
+ }
253
+
254
+ /**
255
+ * return_trace_callback
256
+ * Callback function for Tracer#return_tracepoint. It gets called on
257
+ * RUBY_EVENT_END, RUBY_EVENT_RETURN, and
258
+ * RUBY_EVENT_B_RETURN events. It keeps line tracing consistent when Ruby
259
+ * program counter interleaves files. Everytime called, it checks caller stack
260
+ * frame's file path, if it matches any of the breakpoints, it turns line
261
+ * event tracing back on.
262
+ */
263
+ static void
264
+ return_trace_callback(void *data, rb_trace_arg_t *trace_arg)
265
+ {
266
+ int match_found;
267
+ VALUE self = (VALUE) data;
268
+ VALUE caller_locations;
269
+ VALUE *c_caller_locations;
270
+ VALUE caller_location;
271
+ VALUE caller_path;
272
+ int c_caller_locations_len;
273
+
274
+ ID caller_locations_id;
275
+ ID absolute_path_id;
276
+
277
+ CONST_ID(caller_locations_id, "caller_locations");
278
+ CONST_ID(absolute_path_id, "absolute_path");
279
+
280
+ caller_locations = rb_funcall(rb_mKernel, caller_locations_id, 2, INT2NUM(1), INT2NUM(1));
281
+
282
+
283
+ // Return if current execution stack is too shallow.
284
+ if(!RTEST(caller_locations)) {
285
+ return;
286
+ }
287
+
288
+ c_caller_locations = RARRAY_PTR(caller_locations);
289
+ c_caller_locations_len = RARRAY_LEN(caller_locations);
290
+
291
+ // Make sure caller locations has at least one entry.
292
+ if(c_caller_locations_len == 0) {
293
+ return;
294
+ }
295
+
296
+ caller_location = c_caller_locations[0];
297
+ caller_path = rb_funcall(caller_location, absolute_path_id, 0);
298
+
299
+ // Return if caller location doesn't have absolute path. (i.e. dynamically defined method)
300
+ if(!RTEST(caller_path)) {
301
+ return;
302
+ }
303
+
304
+ match_found = match_breakpoints_files(self, caller_path);
305
+
306
+ if (match_found) {
307
+ enable_line_trace_for_thread(self);
308
+ }
309
+
310
+ return;
311
+ }
312
+
313
+ /**
314
+ * disable_return_trace_for_thread
315
+ * Turn off return events trace hook for a given thread. If no thread is given, it
316
+ * turns off line event trace hook in current thread. It only takes action if
317
+ * the thread has a thread variable "gcloud_return_trace_set" that's true.
318
+ */
319
+ static VALUE
320
+ disable_return_trace_for_thread(VALUE thread)
321
+ {
322
+ VALUE return_trace_set;
323
+ ID return_trace_thread_id;
324
+
325
+ CONST_ID(return_trace_thread_id, "gcloud_return_trace_set");
326
+
327
+ if (!RTEST(thread)) {
328
+ thread = rb_thread_current();
329
+ }
330
+ return_trace_set = rb_ivar_get(thread, return_trace_thread_id);
331
+
332
+ if (RTEST(return_trace_set)) {
333
+ rb_thread_remove_event_hook(thread, (rb_event_hook_func_t)return_trace_callback);
334
+ rb_ivar_set(thread, return_trace_thread_id, Qfalse);
335
+ }
336
+
337
+ return Qnil;
338
+ }
339
+
340
+ /**
341
+ * enable_return_trace_for_thread
342
+ * Turn on return events trace for current thread. Also set a flag
343
+ * "gcloud_return_trace_set" to Qtrue in current thread's thread variable.
344
+ */
345
+ static VALUE
346
+ enable_return_trace_for_thread(VALUE self)
347
+ {
348
+ VALUE current_thread;
349
+ VALUE return_trace_set;
350
+ ID return_trace_thread_id;
351
+
352
+ CONST_ID(return_trace_thread_id, "gcloud_return_trace_set");
353
+
354
+ current_thread = rb_thread_current();
355
+ return_trace_set = rb_ivar_get(current_thread, return_trace_thread_id);
356
+
357
+ if (!RTEST(return_trace_set)) {
358
+ rb_thread_add_event_hook2(current_thread, (rb_event_hook_func_t)return_trace_callback, RETURN_TRACEPOINT_EVENTS, self, RUBY_EVENT_HOOK_FLAG_RAW_ARG | RUBY_EVENT_HOOK_FLAG_SAFE);
359
+ rb_ivar_set(current_thread, return_trace_thread_id, Qtrue);
360
+ }
361
+
362
+ return Qnil;
363
+ }
364
+
365
+ /**
366
+ * file_tracepoint_callback
367
+ * Callback function for Tracer#file_tracepoint. It gets called on
368
+ * RUBY_EVENT_CLASS, RUBY_EVENT_CALL, and RUBY_EVENT_B_CALL
369
+ * events. It check if any breakpoints matches current file the VM program counter
370
+ * is in, and turn on line event tracing for that thread. Otherwise turn off
371
+ * line tracing if in wrong file. The first time it turns on line event tracing,
372
+ * it also turns on Tracer#return_tracepoint to maintain line tracing
373
+ * consistency when file execution interleaves.
374
+ */
375
+ static void
376
+ file_tracepoint_callback(VALUE tracepoint, void *data)
377
+ {
378
+ VALUE self = (VALUE) data;
379
+ rb_trace_arg_t *tracepoint_arg = rb_tracearg_from_tracepoint(tracepoint);
380
+ VALUE tracepoint_path = rb_tracearg_path(tracepoint_arg);
381
+ int match_found;
382
+
383
+ if (!RB_TYPE_P(tracepoint_path, T_STRING))
384
+ return;
385
+
386
+ // Ensure tracepoint_path is absolute path
387
+ tracepoint_path = rb_file_expand_path(tracepoint_path, Qnil);
388
+
389
+ if (!RTEST(tracepoint_path)) {
390
+ return;
391
+ }
392
+
393
+ match_found = match_breakpoints_files(self, tracepoint_path);
394
+
395
+ if (match_found) {
396
+ enable_line_trace_for_thread(self);
397
+ enable_return_trace_for_thread(self);
398
+ }
399
+ else {
400
+ disable_line_trace_for_thread(Qnil);
401
+ }
402
+
403
+ return;
404
+ }
405
+
406
+ #ifdef RUBY_EVENT_FIBER_SWITCH
407
+ static void
408
+ fiber_tracepoint_callback(VALUE tracepoint, void *data)
409
+ {
410
+ VALUE self = (VALUE) data;
411
+ rb_trace_arg_t *tracepoint_arg = rb_tracearg_from_tracepoint(tracepoint);
412
+ VALUE tracepoint_lineno = rb_tracearg_lineno(tracepoint_arg);
413
+ int c_tracepoint_lineno = NUM2INT(tracepoint_lineno);
414
+
415
+ // Only if lineno is greater than 0, then we know this event is triggered from
416
+ // fiber execution, and we blindly starts line_trace.
417
+ if (c_tracepoint_lineno > 0) {
418
+ enable_line_trace_for_thread(self);
419
+ }
420
+
421
+ return;
422
+ }
423
+ #endif
424
+
425
+ /**
426
+ * register_tracepoint
427
+ * Helper function to create a new tracepoint and set the instance varaible on
428
+ * tracer if it doesn't exist already. Returns the existing tracepoint or the
429
+ * newly created tracepoint.
430
+ */
431
+ static VALUE
432
+ register_tracepoint(VALUE self, int event, const char *instance_variable_name, void (*call_back_func)(VALUE, void *))
433
+ {
434
+ VALUE tracepoint = rb_iv_get(self, instance_variable_name);
435
+
436
+ if (event && !RTEST(tracepoint)) {
437
+ tracepoint = rb_tracepoint_new(Qnil, event, call_back_func, (void *)self);
438
+ rb_iv_set(self, instance_variable_name, tracepoint);
439
+ }
440
+
441
+ return tracepoint;
442
+ }
443
+
444
+ /**
445
+ * rb_disable_traces
446
+ * This is implmenetation of Tracer#disable_traces methods. It disables
447
+ * Tracer#file_tracepoint, Tracer#fiber_tracepoint, return event tracing, and
448
+ * line event tracing for all threads.
449
+ */
450
+ static VALUE
451
+ rb_disable_traces(VALUE self)
452
+ {
453
+ VALUE file_tracepoint;
454
+ VALUE fiber_tracepoint;
455
+ VALUE threads;
456
+ VALUE *c_threads;
457
+ int c_threads_len;
458
+ VALUE thread;
459
+ int i;
460
+ ID alive_q_id;
461
+ ID list_id;
462
+
463
+ CONST_ID(alive_q_id, "alive?");
464
+ CONST_ID(list_id, "list");
465
+
466
+ file_tracepoint = rb_iv_get(self, "@file_tracepoint");
467
+ threads = rb_funcall(rb_cThread, list_id, 0);
468
+ c_threads_len = RARRAY_LEN(threads);
469
+ c_threads = RARRAY_PTR(threads);
470
+ UNUSED(fiber_tracepoint);
471
+
472
+ if (RTEST(file_tracepoint) && RTEST(rb_tracepoint_enabled_p(file_tracepoint)))
473
+ rb_tracepoint_disable(file_tracepoint);
474
+
475
+ #ifdef RUBY_EVENT_FIBER_SWITCH
476
+ fiber_tracepoint= rb_iv_get(self, "@fiber_tracepoint");
477
+ if (RTEST(fiber_tracepoint) && RTEST(rb_tracepoint_enabled_p(fiber_tracepoint)))
478
+ rb_tracepoint_disable(fiber_tracepoint);
479
+ #endif
480
+
481
+ for (i = 0; i < c_threads_len; i++) {
482
+ thread = c_threads[i];
483
+ if (RTEST(rb_funcall(thread, alive_q_id, 0))) {
484
+ disable_line_trace_for_thread(thread);
485
+ disable_return_trace_for_thread(thread);
486
+ }
487
+ }
488
+
489
+ return Qnil;
490
+ }
491
+
492
+ /**
493
+ * rb_enable_traces
494
+ * This is the implementation of Tracer#enable_traces methods. It creates
495
+ * the Tracer#file_tracepoint and Tracer#fiber_tracepoint for the first time
496
+ * called. Then it also enables them immediately upon creation.
497
+ */
498
+ static VALUE
499
+ rb_enable_traces(VALUE self)
500
+ {
501
+ VALUE file_tracepoint;
502
+ VALUE fiber_tracepoint;
503
+
504
+ file_tracepoint = register_tracepoint(self, FILE_TRACEPOINT_EVENTS, "@file_tracepoint", file_tracepoint_callback);
505
+ UNUSED(fiber_tracepoint);
506
+
507
+ // Immediately activate file tracepoint and fiber tracepoint
508
+ if (RTEST(file_tracepoint) && !RTEST(rb_tracepoint_enabled_p(file_tracepoint))) {
509
+ rb_tracepoint_enable(file_tracepoint);
510
+ }
511
+ #ifdef RUBY_EVENT_FIBER_SWITCH
512
+ fiber_tracepoint = register_tracepoint(self, RUBY_EVENT_FIBER_SWITCH, "@fiber_tracepoint", fiber_tracepoint_callback);
513
+ if (RTEST(fiber_tracepoint) && !RTEST(rb_tracepoint_enabled_p(fiber_tracepoint))) {
514
+ rb_tracepoint_enable(fiber_tracepoint);
515
+ }
516
+ #endif
517
+ return Qnil;
518
+ }
519
+
520
+ /**
521
+ * rb_disable_traces_for_thread
522
+ * It disables line tracing and return event tracing for current thread.
523
+ */
524
+ static VALUE
525
+ rb_disable_traces_for_thread(VALUE self)
526
+ {
527
+ VALUE thread = rb_thread_current();
528
+ disable_line_trace_for_thread(thread);
529
+ disable_return_trace_for_thread(thread);
530
+
531
+ return Qnil;
532
+ }
533
+
534
+ void
535
+ Init_tracer(VALUE mDebugger)
536
+ {
537
+ VALUE cTracer = rb_define_class_under(mDebugger, "Tracer", rb_cObject);
538
+
539
+ rb_define_method(cTracer, "enable_traces", rb_enable_traces, 0);
540
+ rb_define_method(cTracer, "disable_traces", rb_disable_traces, 0);
541
+ rb_define_method(cTracer, "disable_traces_for_thread", rb_disable_traces_for_thread, 0);
542
+ }