google-cloud-debugger 0.24.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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +8 -0
  3. data/LICENSE +201 -0
  4. data/README.md +56 -0
  5. data/ext/google/cloud/debugger/debugger_c/debugger.c +31 -0
  6. data/ext/google/cloud/debugger/debugger_c/debugger.h +26 -0
  7. data/ext/google/cloud/debugger/debugger_c/evaluator.c +78 -0
  8. data/ext/google/cloud/debugger/debugger_c/evaluator.h +25 -0
  9. data/ext/google/cloud/debugger/debugger_c/extconf.rb +22 -0
  10. data/ext/google/cloud/debugger/debugger_c/tracer.c +478 -0
  11. data/ext/google/cloud/debugger/debugger_c/tracer.h +31 -0
  12. data/lib/google-cloud-debugger.rb +121 -0
  13. data/lib/google/cloud/debugger.rb +379 -0
  14. data/lib/google/cloud/debugger/agent.rb +204 -0
  15. data/lib/google/cloud/debugger/async_actor.rb +290 -0
  16. data/lib/google/cloud/debugger/breakpoint.rb +382 -0
  17. data/lib/google/cloud/debugger/breakpoint/evaluator.rb +1113 -0
  18. data/lib/google/cloud/debugger/breakpoint/source_location.rb +75 -0
  19. data/lib/google/cloud/debugger/breakpoint/stack_frame.rb +109 -0
  20. data/lib/google/cloud/debugger/breakpoint/variable.rb +304 -0
  21. data/lib/google/cloud/debugger/breakpoint_manager.rb +217 -0
  22. data/lib/google/cloud/debugger/credentials.rb +41 -0
  23. data/lib/google/cloud/debugger/debuggee.rb +204 -0
  24. data/lib/google/cloud/debugger/debuggee/app_uniquifier_generator.rb +78 -0
  25. data/lib/google/cloud/debugger/middleware.rb +77 -0
  26. data/lib/google/cloud/debugger/project.rb +135 -0
  27. data/lib/google/cloud/debugger/rails.rb +141 -0
  28. data/lib/google/cloud/debugger/service.rb +130 -0
  29. data/lib/google/cloud/debugger/tracer.rb +165 -0
  30. data/lib/google/cloud/debugger/transmitter.rb +129 -0
  31. data/lib/google/cloud/debugger/v2.rb +15 -0
  32. data/lib/google/cloud/debugger/v2/controller2_client.rb +299 -0
  33. data/lib/google/cloud/debugger/v2/controller2_client_config.json +43 -0
  34. data/lib/google/cloud/debugger/v2/debugger2_client.rb +378 -0
  35. data/lib/google/cloud/debugger/v2/debugger2_client_config.json +53 -0
  36. data/lib/google/cloud/debugger/v2/doc/google/devtools/clouddebugger/v2/data.rb +441 -0
  37. data/lib/google/cloud/debugger/v2/doc/google/devtools/clouddebugger/v2/debugger.rb +151 -0
  38. data/lib/google/cloud/debugger/v2/doc/google/devtools/source/v1/source_context.rb +161 -0
  39. data/lib/google/cloud/debugger/v2/doc/google/protobuf/timestamp.rb +81 -0
  40. data/lib/google/cloud/debugger/version.rb +22 -0
  41. data/lib/google/devtools/clouddebugger/v2/controller_pb.rb +47 -0
  42. data/lib/google/devtools/clouddebugger/v2/controller_services_pb.rb +97 -0
  43. data/lib/google/devtools/clouddebugger/v2/data_pb.rb +105 -0
  44. data/lib/google/devtools/clouddebugger/v2/debugger_pb.rb +74 -0
  45. data/lib/google/devtools/clouddebugger/v2/debugger_services_pb.rb +64 -0
  46. data/lib/google/devtools/source/v1/source_context_pb.rb +89 -0
  47. metadata +300 -0
@@ -0,0 +1,204 @@
1
+ # Copyright 2017 Google Inc. All rights reserved.
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
+ # http://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/async_actor"
17
+ require "google/cloud/debugger/breakpoint_manager"
18
+ require "google/cloud/debugger/debuggee"
19
+ require "google/cloud/debugger/debugger_c"
20
+ require "google/cloud/debugger/tracer"
21
+ require "google/cloud/debugger/transmitter"
22
+
23
+ module Google
24
+ module Cloud
25
+ module Debugger
26
+ ##
27
+ # # Agent
28
+ #
29
+ # The Stackdriver Debugger Agent runs on the same system where a debuggee
30
+ # application is running. The agent is responsible for sending state data,
31
+ # such as the value of program variables and the call stack, to
32
+ # Stackdriver Debugger when the code at a breakpoint location is executed.
33
+ #
34
+ # The Debugger Agent runs in its own child thread when started. It ensures
35
+ # the instrumented application is registered properly and constantly
36
+ # monitors for any active breakpoints. Once the agent gets updated with
37
+ # active breakpoints from Stackdriver Debugger service, it facilitates
38
+ # the breakpoints in application requests thread, then transport the
39
+ # result snapshot back to Stackdriver Debugger service asynchronously.
40
+ #
41
+ # @example
42
+ # require "google/cloud/debugger"
43
+ #
44
+ # debugger = Google::Cloud::Debugger.new
45
+ # agent = debugger.agent
46
+ # agent.start
47
+ #
48
+ class Agent
49
+ ##
50
+ # @private Debugger Agent is an asynchronous actor
51
+ include AsyncActor
52
+
53
+ ##
54
+ # @private The gRPC Service object.
55
+ attr_reader :service
56
+
57
+ ##
58
+ # The gRPC Debuggee representation of the debuggee application. It
59
+ # contains identification information to match running application to
60
+ # specific Cloud Source Repository code base, and correctly group
61
+ # same versions of the debuggee application together through a generated
62
+ # unique identifier.
63
+ # @return [Google::Cloud::Debugger::Debuggee]
64
+ attr_reader :debuggee
65
+
66
+ ##
67
+ # It manages syncing breakpoints between the Debugger Agent and
68
+ # Stackdriver Debugger service
69
+ # @return [Google::Cloud::Debugger::BreakpointManager]
70
+ attr_reader :breakpoint_manager
71
+
72
+ ##
73
+ # It monitors the debuggee application and triggers breakpoint
74
+ # evaluation when breakpoints are set.
75
+ # @return [Google::Cloud::Debugger::Tracer]
76
+ attr_reader :tracer
77
+
78
+ ##
79
+ # It sends evaluated breakpoints snapshot back to Stackdriver Debugger
80
+ # Service.
81
+ # @return [Google::Cloud::Debugger::Transmiter]
82
+ attr_reader :transmitter
83
+
84
+ ##
85
+ # @private The last exception captured in the agent child thread
86
+ attr_reader :last_exception
87
+
88
+ ##
89
+ # Create a new Debugger Agent instance.
90
+ #
91
+ # @param [Google::Cloud::Debugger::Service] service The gRPC Service
92
+ # object
93
+ # @param [String] module_name Name for the debuggee application.
94
+ # Optional.
95
+ # @param [String] module_version Version identifier for the debuggee
96
+ # application. Optional.
97
+ #
98
+ def initialize service, module_name:, module_version:
99
+ super()
100
+
101
+ @service = service
102
+ @debuggee = Debuggee.new service, module_name: module_name,
103
+ module_version: module_version
104
+ @tracer = Debugger::Tracer.new self
105
+ @breakpoint_manager = BreakpointManager.new service
106
+ @breakpoint_manager.on_breakpoints_change =
107
+ method :breakpoints_change_callback
108
+
109
+ @transmitter = Transmitter.new service, self
110
+ end
111
+
112
+ ##
113
+ # Starts the Debugger Agent in a child thread, where debuggee
114
+ # application registration and breakpoints querying will take place.
115
+ # It also starts the transmitter in another child thread.
116
+ #
117
+ def start
118
+ transmitter.start
119
+ async_start
120
+ end
121
+
122
+ ##
123
+ # Stops and terminates the Debugger Agent. It also properly shuts down
124
+ # transmitter and tracer.
125
+ #
126
+ # Once Debugger Agent is stopped, it cannot be started again.
127
+ #
128
+ def stop
129
+ tracer.stop
130
+ transmitter.stop
131
+ async_stop
132
+ end
133
+
134
+ ##
135
+ # Stops the tracer regardless of whether any active breakpoints are
136
+ # present. Once the tracer stops monitoring the debuggee application,
137
+ # the application can return to normal performance.
138
+ def stop_tracer
139
+ tracer.stop
140
+ end
141
+
142
+ ##
143
+ # @private Callback function for AsyncActor module that kicks off
144
+ # the asynchronous job in a loop.
145
+ def run_backgrounder
146
+ sync_breakpoints = ensure_debuggee_registration
147
+
148
+ if sync_breakpoints
149
+ sync_result =
150
+ breakpoint_manager.sync_active_breakpoints debuggee.id
151
+ debuggee.revoke_registration unless sync_result
152
+ end
153
+ rescue => e
154
+ @last_exception = e
155
+ end
156
+
157
+ private
158
+
159
+ ##
160
+ # @private Callback function for breakpoint manager when any updates
161
+ # happens to the list of active breakpoints
162
+ def breakpoints_change_callback active_breakpoints
163
+ if active_breakpoints.empty?
164
+ tracer.stop
165
+ else
166
+ tracer.start
167
+ end
168
+ end
169
+
170
+ ##
171
+ # @private Helper method to register the debuggee application if not
172
+ # registered already.
173
+ def ensure_debuggee_registration
174
+ if debuggee.registered?
175
+ registration_result = true
176
+ else
177
+ registration_result = debuggee.register
178
+ if registration_result
179
+ puts "Debuggee #{debuggee.id} successfully registered"
180
+ end
181
+ end
182
+
183
+ registration_result
184
+ end
185
+
186
+ ##
187
+ # @private Override AsyncActor#async_stop to immediately kill the child
188
+ # thread instead of waiting for it to return, because the breakpoints
189
+ # are queried with a hanging long poll mechanism.
190
+ def async_stop
191
+ @startup_lock.synchronize do
192
+ unless @thread.nil?
193
+ tracer.stop
194
+
195
+ @async_state = :stopped
196
+ @thread.kill
197
+ @thread.join
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,290 @@
1
+ # Copyright 2017 Google Inc. All rights reserved.
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
+ # http://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
+ require "set"
16
+
17
+
18
+ module Google
19
+ module Cloud
20
+ module Debugger
21
+ ##
22
+ # # AsyncActor
23
+ #
24
+ # @private An module that provides a class asynchronous capability when
25
+ # included. It can create a child thread to run background jobs, and
26
+ # help make sure the child thread terminates properly when process
27
+ # is interrupted.
28
+ #
29
+ # To use AsyncActor, the classes that are including this module need to
30
+ # define a run_backgrounder method that does the background job. The
31
+ # classes can then control the child thread job through instance methods
32
+ # like async_start, async_stop, etc.
33
+ #
34
+ # @example
35
+ # class Foo
36
+ # include AsyncActor
37
+ #
38
+ # def initialize
39
+ # super()
40
+ # end
41
+ #
42
+ # def run_backgrounder
43
+ # # run async job
44
+ # end
45
+ # end
46
+ #
47
+ # foo = Foo.new
48
+ # foo.async_start
49
+ #
50
+ module AsyncActor
51
+ include MonitorMixin
52
+
53
+ CLEANUP_TIMEOUT = 10.0
54
+ WAIT_INTERVAL = 1.0
55
+
56
+ @cleanup_list = nil
57
+ @exit_lock = Monitor.new
58
+
59
+ ##
60
+ # @private The async actor state
61
+ attr_reader :async_state
62
+
63
+ ##
64
+ # Starts the child thread and asynchronous job
65
+ def async_start
66
+ ensure_thread
67
+ end
68
+
69
+ ##
70
+ # Nicely ask the child thread to stop by setting the state to
71
+ # :stopping if it's not stopped already.
72
+ #
73
+ # @return [Boolean] False if child thread is already stopped. Otherwise
74
+ # true.
75
+ def async_stop
76
+ ensure_thread
77
+ synchronize do
78
+ if async_state != :stopped
79
+ @async_state = :stopping
80
+ @lock_cond.broadcast
81
+ true
82
+ else
83
+ false
84
+ end
85
+ end
86
+ end
87
+
88
+ ##
89
+ # Set the state to :suspend if the current state is :running.
90
+ #
91
+ # @return [Boolean] Returns true if the state has been set to
92
+ # :suspended. Otherwise return false.
93
+ #
94
+ def async_suspend
95
+ ensure_thread
96
+ synchronize do
97
+ if async_state == :running
98
+ @async_state = :suspended
99
+ @lock_cond.broadcast
100
+ true
101
+ else
102
+ false
103
+ end
104
+ end
105
+ end
106
+
107
+ ##
108
+ # Set the state to :running if the current state is :suspended.
109
+ #
110
+ # @return [Boolean] True if the state has been set to :running.
111
+ # Otherwise return false.
112
+ #
113
+ def async_resume
114
+ ensure_thread
115
+ synchronize do
116
+ if async_state == :suspended
117
+ @async_state = :running
118
+ @lock_cond.broadcast
119
+ true
120
+ else
121
+ false
122
+ end
123
+ end
124
+ end
125
+
126
+ ##
127
+ # Check if async job is running
128
+ #
129
+ # @return [Boolean] True if state equals :running. Otherwise false.
130
+ def async_running?
131
+ synchronize do
132
+ async_state == :running
133
+ end
134
+ end
135
+
136
+ ##
137
+ # Check if async job is suspended
138
+ #
139
+ # @return [Boolean] True if state equals :suspended. Otherwise false.
140
+ def async_suspended?
141
+ synchronize do
142
+ async_state == :suspended
143
+ end
144
+ end
145
+
146
+ ##
147
+ # Check if async job is working.
148
+ #
149
+ # @return [Boolean] True if state is either :running or :suspended.
150
+ # Otherwise false.
151
+ def async_working?
152
+ synchronize do
153
+ async_state == :suspended || async_state == :running
154
+ end
155
+ end
156
+
157
+ ##
158
+ # Check if async job has stopped
159
+ #
160
+ # @return [Boolean] True if state equals :stopped. Otherwise false.
161
+ def async_stopped?
162
+ synchronize do
163
+ async_state == :stopped
164
+ end
165
+ end
166
+
167
+ ##
168
+ # Ask async job to stop. Then forcefully kill thread if it doesn't stop
169
+ # after timeout if needed.
170
+ #
171
+ # @param [Boolean] force If true, forcefully kill thread after timeout.
172
+ # Default to false.
173
+ #
174
+ # @return [Symbol] :stopped if async job already stopped. :waited if
175
+ # async job terminates within timeout range. :timeout if async job
176
+ # doesn't terminate after timeout. :forced if thread is killed by
177
+ # force after timeout.
178
+ def async_stop! timeout, force: false
179
+ return :stopped unless async_stop
180
+ return :waited if wait_until_async_stopped timeout
181
+ return :timeout unless force
182
+ @thread.kill
183
+ @thread.join
184
+ :forced
185
+ end
186
+
187
+ ##
188
+ # @private Cleanup this async job when process exists
189
+ #
190
+ def self.register_for_cleanup actor
191
+ @exit_lock.synchronize do
192
+ unless @cleanup_list
193
+ @cleanup_list = []
194
+ at_exit { run_cleanup }
195
+ end
196
+ @cleanup_list.push actor
197
+ end
198
+ end
199
+
200
+ ##
201
+ # @private Take this async job off exit cleanup list
202
+ #
203
+ def self.unregister_for_cleanup actor
204
+ @exit_lock.synchronize do
205
+ @cleanup_list.delete actor if @cleanup_list
206
+ end
207
+ end
208
+
209
+ ##
210
+ # @private Cleanup the async job
211
+ #
212
+ def self.run_cleanup
213
+ @exit_lock.synchronize do
214
+ if @cleanup_list
215
+ until @cleanup_list.empty?
216
+ @cleanup_list.shift.async_stop! CLEANUP_TIMEOUT, force: true
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ private_class_method :run_cleanup
223
+
224
+ private
225
+
226
+ ##
227
+ # @private Helper method to async_stop! to wait for async job to
228
+ # terminate.
229
+ #
230
+ # @return [Boolean] True if async job terminated. False if timeout.
231
+ #
232
+ def wait_until_async_stopped timeout = nil
233
+ ensure_thread
234
+ deadline = timeout ? ::Time.new.to_f + timeout : nil
235
+ synchronize do
236
+ until async_state == :stopped
237
+ cur_time = ::Time.new.to_f
238
+ return false if deadline && cur_time >= deadline
239
+ interval = deadline ? deadline - cur_time : WAIT_INTERVAL
240
+ interval = WAIT_INTERVAL if interval > WAIT_INTERVAL
241
+ @lock_cond.wait interval
242
+ end
243
+ end
244
+ true
245
+ end
246
+
247
+ ##
248
+ # @private Constructor to initialize MonitorMixin
249
+ #
250
+ def initialize
251
+ super()
252
+ @startup_lock = Mutex.new
253
+ end
254
+
255
+ ##
256
+ # @private Wrapper method for running the async job. It requires classes
257
+ # that include AsyncActor module to define a run_backgrounder method.
258
+ # Then it runs a loop that checks for the state is workable (:running
259
+ # or :suspended), which calls the run_backgrounder method. It ensures
260
+ # the state variable gets set to :stopped when this method exists.
261
+ def async_run_job
262
+ fail "run_backgrounder method not defined" unless
263
+ respond_to? :run_backgrounder
264
+ run_backgrounder while async_working?
265
+ ensure
266
+ @async_state = :stopped
267
+ end
268
+
269
+ ##
270
+ # @private Ensures the child thread is started and kick off the job
271
+ # to run async_run_job. Also make calls register_for_cleanup on the
272
+ # async job to make sure it exits properly.
273
+ def ensure_thread
274
+ fail "async_actor not initialized" if @startup_lock.nil?
275
+ @startup_lock.synchronize do
276
+ if (@thread.nil? || !@thread.alive?) && @async_state != :stopped
277
+ @lock_cond = new_cond
278
+ AsyncActor.register_for_cleanup self
279
+ @thread = Thread.new do
280
+ async_run_job
281
+ AsyncActor.unregister_for_cleanup self
282
+ end
283
+ @async_state = :running
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end