google-cloud-debugger 0.40.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +18 -0
- data/AUTHENTICATION.md +178 -0
- data/CHANGELOG.md +233 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CONTRIBUTING.md +188 -0
- data/INSTRUMENTATION.md +115 -0
- data/LICENSE +201 -0
- data/LOGGING.md +32 -0
- data/OVERVIEW.md +266 -0
- data/TROUBLESHOOTING.md +31 -0
- data/ext/google/cloud/debugger/debugger_c/debugger.c +31 -0
- data/ext/google/cloud/debugger/debugger_c/debugger.h +26 -0
- data/ext/google/cloud/debugger/debugger_c/evaluator.c +115 -0
- data/ext/google/cloud/debugger/debugger_c/evaluator.h +25 -0
- data/ext/google/cloud/debugger/debugger_c/extconf.rb +22 -0
- data/ext/google/cloud/debugger/debugger_c/tracer.c +542 -0
- data/ext/google/cloud/debugger/debugger_c/tracer.h +25 -0
- data/lib/google-cloud-debugger.rb +181 -0
- data/lib/google/cloud/debugger.rb +259 -0
- data/lib/google/cloud/debugger/agent.rb +255 -0
- data/lib/google/cloud/debugger/backoff.rb +70 -0
- data/lib/google/cloud/debugger/breakpoint.rb +443 -0
- data/lib/google/cloud/debugger/breakpoint/evaluator.rb +1099 -0
- data/lib/google/cloud/debugger/breakpoint/source_location.rb +74 -0
- data/lib/google/cloud/debugger/breakpoint/stack_frame.rb +109 -0
- data/lib/google/cloud/debugger/breakpoint/status_message.rb +93 -0
- data/lib/google/cloud/debugger/breakpoint/validator.rb +92 -0
- data/lib/google/cloud/debugger/breakpoint/variable.rb +595 -0
- data/lib/google/cloud/debugger/breakpoint/variable_table.rb +96 -0
- data/lib/google/cloud/debugger/breakpoint_manager.rb +311 -0
- data/lib/google/cloud/debugger/credentials.rb +50 -0
- data/lib/google/cloud/debugger/debuggee.rb +222 -0
- data/lib/google/cloud/debugger/debuggee/app_uniquifier_generator.rb +76 -0
- data/lib/google/cloud/debugger/logpoint.rb +98 -0
- data/lib/google/cloud/debugger/middleware.rb +200 -0
- data/lib/google/cloud/debugger/project.rb +110 -0
- data/lib/google/cloud/debugger/rails.rb +174 -0
- data/lib/google/cloud/debugger/request_quota_manager.rb +95 -0
- data/lib/google/cloud/debugger/service.rb +88 -0
- data/lib/google/cloud/debugger/snappoint.rb +208 -0
- data/lib/google/cloud/debugger/tracer.rb +137 -0
- data/lib/google/cloud/debugger/transmitter.rb +199 -0
- data/lib/google/cloud/debugger/version.rb +22 -0
- metadata +353 -0
@@ -0,0 +1,208 @@
|
|
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 "google/cloud/debugger/breakpoint"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Debugger
|
21
|
+
##
|
22
|
+
# # Snappoint
|
23
|
+
#
|
24
|
+
# A kind of {Google::Cloud::Debugger::Breakpoint} that can be evaluated
|
25
|
+
# to capture the state of the program at time of evaluation. This is
|
26
|
+
# essentially a {Google::Cloud::Debugger::Breakpoint} with action attrubte
|
27
|
+
# set to `:CAPTURE`
|
28
|
+
#
|
29
|
+
class Snappoint < Breakpoint
|
30
|
+
##
|
31
|
+
# Max number of top stacks to collect local variables information
|
32
|
+
STACK_EVAL_DEPTH = 5
|
33
|
+
|
34
|
+
##
|
35
|
+
# Max size of payload a Snappoint collects
|
36
|
+
MAX_PAYLOAD_SIZE = 32768 # 32KB
|
37
|
+
|
38
|
+
##
|
39
|
+
# @private Max size an evaluated expression variable is allowed to be
|
40
|
+
MAX_EXPRESSION_LIMIT = 32768 # 32KB
|
41
|
+
|
42
|
+
##
|
43
|
+
# @private Max size a normal evaluated variable is allowed to be
|
44
|
+
MAX_VAR_LIMIT = 1024 # 1KB
|
45
|
+
|
46
|
+
##
|
47
|
+
# @private Construct a new Snappoint instance.
|
48
|
+
def initialize *args
|
49
|
+
super
|
50
|
+
|
51
|
+
init_var_table
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# @private Initialize the variable table by inserting a buffer full
|
56
|
+
# variable at index 0. This variable will be shared by other variable
|
57
|
+
# evaluations if this Snappoint exceeds size limit.
|
58
|
+
def init_var_table
|
59
|
+
return if @variable_table[0] &&
|
60
|
+
@variable_table[0].buffer_full_variable?
|
61
|
+
|
62
|
+
buffer_full_var = Variable.buffer_full_variable
|
63
|
+
@variable_table.variables.unshift buffer_full_var
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Evaluate the breakpoint unless it's already marked as completed.
|
68
|
+
# Store evaluted expressions and stack frame variables in
|
69
|
+
# @evaluated_expressions, @stack_frames. Mark breakpoint complete if
|
70
|
+
# successfully evaluated.
|
71
|
+
#
|
72
|
+
# @param [Array<Binding>] call_stack_bindings An array of Ruby Binding
|
73
|
+
# objects, from the call stack that leads to the triggering of the
|
74
|
+
# breakpoints.
|
75
|
+
#
|
76
|
+
# @return [Boolean] True if evaluated successfully; false otherwise.
|
77
|
+
#
|
78
|
+
def evaluate call_stack_bindings
|
79
|
+
synchronize do
|
80
|
+
top_binding = call_stack_bindings[0]
|
81
|
+
|
82
|
+
return false if complete? || !check_condition(top_binding)
|
83
|
+
|
84
|
+
begin
|
85
|
+
eval_expressions top_binding
|
86
|
+
eval_call_stack call_stack_bindings
|
87
|
+
|
88
|
+
complete
|
89
|
+
rescue StandardError
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# @private Evaluates the breakpoint expressions at the point that
|
99
|
+
# triggered the breakpoint. The expressions subject to the read-only
|
100
|
+
# rules. If the expressions do any write operations, the evaluations
|
101
|
+
# abort and show an error message in place of the real result.
|
102
|
+
#
|
103
|
+
# @param [Binding] bind The binding object from the context
|
104
|
+
#
|
105
|
+
def eval_expressions bind
|
106
|
+
@evaluated_expressions = []
|
107
|
+
|
108
|
+
expressions.each do |expression|
|
109
|
+
eval_result = Evaluator.readonly_eval_expression bind, expression
|
110
|
+
|
111
|
+
if eval_result.is_a?(Exception) &&
|
112
|
+
eval_result.instance_variable_get(:@mutation_cause)
|
113
|
+
evaluated_var = Variable.new
|
114
|
+
evaluated_var.name = expression
|
115
|
+
evaluated_var.set_error_state \
|
116
|
+
"Error: #{eval_result.message}",
|
117
|
+
refers_to: StatusMessage::VARIABLE_VALUE
|
118
|
+
else
|
119
|
+
evaluated_var = convert_variable eval_result,
|
120
|
+
name: expression,
|
121
|
+
limit: MAX_EXPRESSION_LIMIT
|
122
|
+
end
|
123
|
+
|
124
|
+
@evaluated_expressions << evaluated_var
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# @private Evaluates call stack. Collects function name and location of
|
130
|
+
# each frame from given binding objects. Collects local variable
|
131
|
+
# information from top frames.
|
132
|
+
#
|
133
|
+
# @param [Array<Binding>] call_stack_bindings A list of binding
|
134
|
+
# objects that come from each of the call stack frames.
|
135
|
+
# @return [Array<Google::Cloud::Debugger::Breakpoint::StackFrame>]
|
136
|
+
# A list of StackFrame objects that represent state of the
|
137
|
+
# call stack
|
138
|
+
#
|
139
|
+
def eval_call_stack call_stack_bindings
|
140
|
+
@stack_frames = []
|
141
|
+
|
142
|
+
call_stack_bindings.each_with_index do |frame_binding, i|
|
143
|
+
frame_info = StackFrame.new.tap do |sf|
|
144
|
+
sf.function = frame_binding.eval("__method__").to_s
|
145
|
+
sf.location = SourceLocation.new.tap do |l|
|
146
|
+
l.path =
|
147
|
+
frame_binding.eval "::File.absolute_path(__FILE__)"
|
148
|
+
l.line = frame_binding.eval "__LINE__"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
@stack_frames << frame_info
|
153
|
+
|
154
|
+
next if i >= STACK_EVAL_DEPTH
|
155
|
+
|
156
|
+
frame_binding.local_variables.each do |local_var_name|
|
157
|
+
local_var = frame_binding.local_variable_get local_var_name
|
158
|
+
var = convert_variable local_var, name: local_var_name,
|
159
|
+
limit: MAX_VAR_LIMIT
|
160
|
+
|
161
|
+
frame_info.locals << var
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
##
|
169
|
+
# @private Compute the total size of all the evaluated variables in
|
170
|
+
# this breakpoint.
|
171
|
+
def calculate_total_size
|
172
|
+
result = evaluated_expressions.inject 0 do |sum, exp|
|
173
|
+
sum + exp.payload_size
|
174
|
+
end
|
175
|
+
|
176
|
+
stack_frames.each do |stack_frame|
|
177
|
+
result = stack_frame.locals.inject result do |sum, local|
|
178
|
+
sum + local.payload_size
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
result = variable_table.variables.inject result do |sum, var|
|
183
|
+
sum + var.payload_size
|
184
|
+
end
|
185
|
+
|
186
|
+
result
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# @private Translate a Ruby variable into a {Breakpoint::Variable}.
|
191
|
+
# If the existing evaluated variables already exceed maximum allowed
|
192
|
+
# size, then return a buffer full warning variable instead.
|
193
|
+
def convert_variable source, name: nil, limit: nil
|
194
|
+
current_total_size = calculate_total_size
|
195
|
+
var = Variable.from_rb_var source, name: name, limit: limit,
|
196
|
+
var_table: variable_table
|
197
|
+
|
198
|
+
if (current_total_size + var.payload_size <= MAX_PAYLOAD_SIZE) ||
|
199
|
+
var.reference_variable?
|
200
|
+
var
|
201
|
+
else
|
202
|
+
Breakpoint::Variable.buffer_full_variable variable_table, name: name
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,137 @@
|
|
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 "binding_of_caller"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Debugger
|
21
|
+
##
|
22
|
+
# # Tracer
|
23
|
+
#
|
24
|
+
# When active breakpoints are set for the debugger, the tracer monitors
|
25
|
+
# the running Ruby application and triggers evaluation when the code is
|
26
|
+
# executed at the breakpoint locations.
|
27
|
+
#
|
28
|
+
# The tracer tracks the running application using several Ruby TracePoints
|
29
|
+
# and C level Ruby debugging API.
|
30
|
+
#
|
31
|
+
class Tracer
|
32
|
+
##
|
33
|
+
# The debugger agent this tracer belongs to
|
34
|
+
# @return [Google::Cloud::Debugger::Agent]
|
35
|
+
attr_reader :agent
|
36
|
+
|
37
|
+
##
|
38
|
+
# @private File tracing point that enables line tracing when program
|
39
|
+
# counter enters a file that contains breakpoints
|
40
|
+
attr_reader :file_tracepoint
|
41
|
+
|
42
|
+
##
|
43
|
+
# @private Fiber tracing point that enables line tracing when program
|
44
|
+
# counter enters a file that contains breakpoints through fiber
|
45
|
+
# switching
|
46
|
+
attr_reader :fiber_tracepoint
|
47
|
+
|
48
|
+
##
|
49
|
+
# @private A nested hash structure represent all the active breakpoints.
|
50
|
+
# The structure is optimized for fast access. For example:
|
51
|
+
# {
|
52
|
+
# "path/to/file.rb" => { # The absolute file path
|
53
|
+
# 123 => [ # The line number in file
|
54
|
+
# <Google::Cloud::Debugger::Breakpoint> # List of breakpoints
|
55
|
+
# ]
|
56
|
+
# }
|
57
|
+
# }
|
58
|
+
attr_reader :breakpoints_cache
|
59
|
+
|
60
|
+
##
|
61
|
+
# @private Construct a new instance of Tracer
|
62
|
+
def initialize agent
|
63
|
+
@agent = agent
|
64
|
+
@file_tracepoint = nil
|
65
|
+
@fiber_tracepoint = nil
|
66
|
+
@breakpoints_cache = {}
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Update tracer's private breakpoints cache with the list of active
|
71
|
+
# breakpoints from BreakpointManager.
|
72
|
+
#
|
73
|
+
# This methood is atomic for thread safety purpose.
|
74
|
+
def update_breakpoints_cache
|
75
|
+
active_breakpoints = agent.breakpoint_manager.active_breakpoints.dup
|
76
|
+
breakpoints_hash = {}
|
77
|
+
|
78
|
+
active_breakpoints.each do |active_breakpoint|
|
79
|
+
breakpoint_line = active_breakpoint.line
|
80
|
+
breakpoint_path = active_breakpoint.full_path
|
81
|
+
breakpoints_hash[breakpoint_path] ||= {}
|
82
|
+
breakpoints_hash[breakpoint_path][breakpoint_line] ||= []
|
83
|
+
breakpoints_hash[breakpoint_path][breakpoint_line].push(
|
84
|
+
active_breakpoint
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Tracer is explicitly designed to not have a lock. This should be the
|
89
|
+
# only place writing @breakpoints_cache to ensure thread safety.
|
90
|
+
@breakpoints_cache = breakpoints_hash
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Callback function when a set of breakpoints are hit. Handover the hit
|
95
|
+
# breakpoint to breakpoint_manager to be evaluated.
|
96
|
+
def breakpoints_hit breakpoints, call_stack_bindings
|
97
|
+
breakpoints.each do |breakpoint|
|
98
|
+
# Stop evaluating breakpoints if we have quotas and the quotas are
|
99
|
+
# met.
|
100
|
+
break if agent.quota_manager && !agent.quota_manager.more?
|
101
|
+
|
102
|
+
next if breakpoint.nil? || breakpoint.complete?
|
103
|
+
|
104
|
+
time_begin = Time.now
|
105
|
+
|
106
|
+
agent.breakpoint_manager.breakpoint_hit breakpoint,
|
107
|
+
call_stack_bindings
|
108
|
+
|
109
|
+
# Report time and resource consumption to quota manager
|
110
|
+
if agent.quota_manager.respond_to? :consume
|
111
|
+
agent.quota_manager.consume time: Time.now - time_begin
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
update_breakpoints_cache
|
116
|
+
|
117
|
+
# Disable all trace points and tracing if all breakpoints are complete
|
118
|
+
disable_traces if @breakpoints_cache.empty?
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Get the sync the breakpoints cache with BreakpointManager. Start
|
123
|
+
# tracing and monitoring if there are any breakpoints.
|
124
|
+
def start
|
125
|
+
update_breakpoints_cache
|
126
|
+
enable_traces unless breakpoints_cache.empty?
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Stops all tracing.
|
131
|
+
def stop
|
132
|
+
disable_traces
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,199 @@
|
|
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 "concurrent"
|
17
|
+
require "google/cloud/errors"
|
18
|
+
|
19
|
+
module Google
|
20
|
+
module Cloud
|
21
|
+
module Debugger
|
22
|
+
##
|
23
|
+
# # TransmitterError
|
24
|
+
#
|
25
|
+
# Used to indicate a problem submitting breakpoints. This can occur when
|
26
|
+
# there are not enough resources allocated for the amount of usage, or
|
27
|
+
# when the calling the API returns an error.
|
28
|
+
#
|
29
|
+
class TransmitterError < Google::Cloud::Error
|
30
|
+
# @!attribute [r] breakpoint
|
31
|
+
# @return [Array<Google::Cloud::Debugger::Breakpoint>] The
|
32
|
+
# individual error event that was not submitted to Stackdriver
|
33
|
+
# Debugger service.
|
34
|
+
attr_reader :breakpoint
|
35
|
+
|
36
|
+
def initialize message, breakpoint = nil
|
37
|
+
super message
|
38
|
+
@breakpoint = breakpoint
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# # Transmitter
|
44
|
+
#
|
45
|
+
# Responsible for submit evaluated breakpoints back to Stackdriver
|
46
|
+
# Debugger service asynchronously. It maintains a thread pool.
|
47
|
+
#
|
48
|
+
# The transmitter is controlled by the debugger agent it belongs to.
|
49
|
+
# Debugger agent submits evaluated breakpoint asynchronously, and the
|
50
|
+
# transmitter submits the breakpoints to Stackdriver Debugger service.
|
51
|
+
#
|
52
|
+
class Transmitter
|
53
|
+
##
|
54
|
+
# @private The gRPC Service object and thread pool.
|
55
|
+
attr_reader :service, :thread_pool
|
56
|
+
|
57
|
+
##
|
58
|
+
# The debugger agent this transmiter belongs to
|
59
|
+
# @return [Google::Cloud::Debugger::Agent]
|
60
|
+
attr_accessor :agent
|
61
|
+
|
62
|
+
##
|
63
|
+
# Maximum backlog size for this transmitter's queue
|
64
|
+
attr_accessor :max_queue
|
65
|
+
alias max_queue_size max_queue
|
66
|
+
alias max_queue_size= max_queue=
|
67
|
+
|
68
|
+
##
|
69
|
+
# Maximum threads used in the thread pool
|
70
|
+
attr_accessor :threads
|
71
|
+
|
72
|
+
##
|
73
|
+
# @private Creates a new Transmitter instance.
|
74
|
+
def initialize agent, service, max_queue: 1000, threads: 10
|
75
|
+
@agent = agent
|
76
|
+
@service = service
|
77
|
+
|
78
|
+
@max_queue = max_queue
|
79
|
+
@threads = threads
|
80
|
+
|
81
|
+
@thread_pool = Concurrent::ThreadPoolExecutor.new \
|
82
|
+
max_threads: @threads, max_queue: @max_queue
|
83
|
+
|
84
|
+
@error_callbacks = []
|
85
|
+
|
86
|
+
# Make sure all queued calls are completed when process exits.
|
87
|
+
at_exit { stop }
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Enqueue an evaluated breakpoint to be submitted by the transmitter.
|
92
|
+
#
|
93
|
+
# @raise [TransmitterError] if there are no resources available to make
|
94
|
+
# queue the API call on the thread pool.
|
95
|
+
def submit breakpoint
|
96
|
+
Concurrent::Promises.future_on @thread_pool, breakpoint do |bp|
|
97
|
+
submit_sync bp
|
98
|
+
end
|
99
|
+
rescue Concurrent::RejectedExecutionError => e
|
100
|
+
raise TransmitterError.new(
|
101
|
+
"Error asynchronously submitting breakpoint: #{e.message}",
|
102
|
+
breakpoint
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Starts the transmitter and its thread pool.
|
108
|
+
#
|
109
|
+
# @return [Transmitter] returns self so calls can be chained.
|
110
|
+
def start
|
111
|
+
# no-op
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# Stops the transmitter and its thread pool. Once stopped, cannot be
|
117
|
+
# started again.
|
118
|
+
#
|
119
|
+
# @return [Transmitter] returns self so calls can be chained.
|
120
|
+
def stop timeout = nil
|
121
|
+
if @thread_pool
|
122
|
+
@thread_pool.shutdown
|
123
|
+
@thread_pool.wait_for_termination timeout
|
124
|
+
end
|
125
|
+
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Whether the transmitter has been started.
|
131
|
+
#
|
132
|
+
# @return [boolean] `true` when started, `false` otherwise.
|
133
|
+
#
|
134
|
+
def started?
|
135
|
+
@thread_pool.running? if @thread_pool
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Whether the transmitter has been stopped.
|
140
|
+
#
|
141
|
+
# @return [boolean] `true` when stopped, `false` otherwise.
|
142
|
+
#
|
143
|
+
def stopped?
|
144
|
+
!started?
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Register to be notified of errors when raised.
|
149
|
+
#
|
150
|
+
# If an unhandled error has occurred the transmitter will attempt to
|
151
|
+
# recover from the error and resume submitting breakpoints.
|
152
|
+
#
|
153
|
+
# Multiple error handlers can be added.
|
154
|
+
#
|
155
|
+
# @yield [callback] The block to be called when an error is raised.
|
156
|
+
# @yieldparam [Exception] error The error raised.
|
157
|
+
#
|
158
|
+
def on_error &block
|
159
|
+
@error_callbacks << block
|
160
|
+
end
|
161
|
+
|
162
|
+
protected
|
163
|
+
|
164
|
+
# Calls all error callbacks.
|
165
|
+
def error! error
|
166
|
+
error_callbacks = @error_callbacks
|
167
|
+
error_callbacks = default_error_callbacks if error_callbacks.empty?
|
168
|
+
error_callbacks.each { |error_callback| error_callback.call error }
|
169
|
+
end
|
170
|
+
|
171
|
+
def default_error_callbacks
|
172
|
+
# This is memoized to reduce calls to the configuration.
|
173
|
+
@default_error_callbacks ||= begin
|
174
|
+
error_cb = Google::Cloud::Debugger.configure.on_error
|
175
|
+
error_cb ||= Google::Cloud.configure.on_error
|
176
|
+
if error_cb
|
177
|
+
[error_cb]
|
178
|
+
else
|
179
|
+
[]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def submit_sync breakpoint
|
185
|
+
service.update_active_breakpoint agent.debuggee.id, breakpoint
|
186
|
+
rescue StandardError => e
|
187
|
+
sync_error = TransmitterError.new(
|
188
|
+
"Error asynchronously transmitting breakpoint: #{e.message}",
|
189
|
+
breakpoint
|
190
|
+
)
|
191
|
+
# Manually set backtrace so we don't have to raise
|
192
|
+
sync_error.set_backtrace caller
|
193
|
+
|
194
|
+
error! sync_error
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|