google-cloud-debugger 0.40.0

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.
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
+ }