google-cloud-debugger 0.26.1 → 0.27.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7a8f4cd45e74a4f18bf53c08fb05284529937c6b
4
- data.tar.gz: '0718436023043859a8c9b958dc43bb623fb7ec79'
3
+ metadata.gz: 51d29e7310e1ea9e0f409ee2e2198d52af61c163
4
+ data.tar.gz: 0e8474858473a49f70b06481f281c98b79e0bd70
5
5
  SHA512:
6
- metadata.gz: 323fde821b65e2167e7ea0b56f2ff988203d62d5391032890be39636bec869750e4bf701a7756b50b59b7b9eb11c71819a8a0df3830574304b7b3d38e1cfa595
7
- data.tar.gz: e68aa3d18c0aaf72db470b4dba4e1ecf439da21b43aeb081a552a49d8da093121795ffd32beec52fda075b68a90564cc70a88b579fca4008b1f2b6386032920c
6
+ metadata.gz: a1e9da07abc025976a843eee2a93d545a09beedac8b7bfc34345ffd77e676ad17a9827fcafd285be65a05ed2d0adfd5380f2f06a12bcaa63b8cf7a000ac88eed
7
+ data.tar.gz: b91cc538bbdc514d8105ae0220c482df52be13539c36ad3cb50bb4d0db071a54c146a687c5a34af40a36de04ddd012b283b26b2a309d0d30ef616450c27c33f9
@@ -23,19 +23,24 @@ eval_trace_callback(void *data, rb_trace_arg_t *trace_arg)
23
23
  {
24
24
  rb_event_flag_t event = rb_tracearg_event_flag(trace_arg);
25
25
  VALUE evaluator = (VALUE)data;
26
- VALUE klass = rb_tracearg_defined_class(trace_arg);
27
- VALUE obj = rb_tracearg_self(trace_arg);
28
- VALUE method_id = rb_tracearg_method_id(trace_arg);
26
+ VALUE klass;
27
+ VALUE obj;
28
+ VALUE method_id;
29
29
  ID trace_func_cb_id;
30
30
  ID trace_c_func_cb_id;
31
31
 
32
32
  CONST_ID(trace_func_cb_id, "trace_func_callback");
33
33
  CONST_ID(trace_c_func_cb_id, "trace_c_func_callback");
34
34
 
35
+ obj = rb_tracearg_self(trace_arg);
36
+ method_id = rb_tracearg_method_id(trace_arg);
37
+
35
38
  if (event & RUBY_EVENT_CALL) {
36
39
  rb_funcall(evaluator, trace_func_cb_id, 2, obj, method_id);
37
40
  }
38
41
  if (event & RUBY_EVENT_C_CALL) {
42
+ klass = rb_tracearg_defined_class(trace_arg);
43
+
39
44
  rb_funcall(evaluator, trace_c_func_cb_id, 3, obj, klass, method_id);
40
45
  }
41
46
 
@@ -144,17 +144,13 @@ line_trace_callback(rb_event_flag_t event, VALUE data, VALUE obj, ID mid, VALUE
144
144
  VALUE trace_binding;
145
145
  VALUE call_stack_bindings;
146
146
 
147
- int i;
148
147
  VALUE matching_result = match_breakpoints(self, c_trace_path, c_trace_lineno);
149
- VALUE *c_matching_breakpoints;
150
- VALUE matching_breakpoint;
151
- int matching_breakpoints_len;
152
148
 
153
149
  ID callers_id;
154
- ID breakpoint_hit_id;
150
+ ID breakpoints_hit_id;
155
151
 
156
152
  CONST_ID(callers_id, "callers");
157
- CONST_ID(breakpoint_hit_id, "breakpoint_hit");
153
+ CONST_ID(breakpoints_hit_id, "breakpoints_hit");
158
154
 
159
155
  // If matching result isn't an array, it means we're in completely wrong file,
160
156
  // or not on the right line. Turn line tracing off if we're in wrong file.
@@ -165,17 +161,11 @@ line_trace_callback(rb_event_flag_t event, VALUE data, VALUE obj, ID mid, VALUE
165
161
  return;
166
162
  }
167
163
 
168
- c_matching_breakpoints = RARRAY_PTR(matching_result);
169
- matching_breakpoints_len = RARRAY_LEN(matching_result);
170
164
  trace_binding = rb_binding_new();
171
165
  call_stack_bindings = rb_funcall(trace_binding, callers_id, 0);
172
166
  rb_ary_pop(call_stack_bindings);
173
167
 
174
- // Evaluate each of the matching breakpoint
175
- for (i = 0; i < matching_breakpoints_len; i++) {
176
- matching_breakpoint = c_matching_breakpoints[i];
177
- rb_funcall(self, breakpoint_hit_id, 2, matching_breakpoint, call_stack_bindings);
178
- }
168
+ rb_funcall(self, breakpoints_hit_id, 2, matching_result, call_stack_bindings);
179
169
 
180
170
  return;
181
171
  }
@@ -90,6 +90,19 @@ module Google
90
90
  # The logger used to write the results of Logpoints.
91
91
  attr_accessor :logger
92
92
 
93
+ ##
94
+ # A quota tracking object helps tracking resource consumption during
95
+ # evaluations.
96
+ attr_accessor :quota_manager
97
+
98
+ ##
99
+ # Absolute path to the debuggee Ruby application root directory. The
100
+ # Stackdriver Debugger service creates canonical breakpoints with only
101
+ # relative path. So the debugger agent combines the relative path to
102
+ # the application directory to trace and evaluate breakpoints.
103
+ # @return [String]
104
+ attr_accessor :app_root
105
+
93
106
  ##
94
107
  # @private The last exception captured in the agent child thread
95
108
  attr_reader :last_exception
@@ -102,11 +115,13 @@ module Google
102
115
  # @param [Google::Cloud::Logging::Logger] logger The logger used
103
116
  # to write the results of Logpoints.
104
117
  # @param [String] module_name Name for the debuggee application.
105
- # Optional.
106
118
  # @param [String] module_version Version identifier for the debuggee
107
- # application. Optional.
119
+ # application.
120
+ # @param [String] app_root Absolute path to the root directory of
121
+ # the debuggee application. Default to Rack root.
108
122
  #
109
- def initialize service, logger: nil, module_name:, module_version:
123
+ def initialize service, logger: nil, module_name:, module_version:,
124
+ app_root: nil
110
125
  super()
111
126
 
112
127
  @service = service
@@ -121,6 +136,9 @@ module Google
121
136
 
122
137
  @logger = logger || default_logger
123
138
 
139
+ @app_root = app_root
140
+ @app_root ||= Rack::Directory.new("").root if defined? Rack::Directory
141
+
124
142
  # Agent actor thread needs to force exit immediately.
125
143
  set_cleanup_options timeout: 0
126
144
  end
@@ -17,11 +17,21 @@ require "time"
17
17
  require "google/cloud/debugger/breakpoint/evaluator"
18
18
  require "google/cloud/debugger/breakpoint/source_location"
19
19
  require "google/cloud/debugger/breakpoint/stack_frame"
20
+ require "google/cloud/debugger/breakpoint/status_message"
21
+ require "google/cloud/debugger/breakpoint/validator"
20
22
  require "google/cloud/debugger/breakpoint/variable"
23
+ require "google/cloud/debugger/breakpoint/variable_table"
21
24
 
22
25
  module Google
23
26
  module Cloud
24
27
  module Debugger
28
+ ##
29
+ # # Breakpoint
30
+ #
31
+ # Abstract class that represents a breakpoint, which can be set and
32
+ # triggered in a debuggee application. Maps to gRPC struct
33
+ # {Google::Devtools::Clouddebugger::V2::Breakpoint}.
34
+ #
25
35
  class Breakpoint
26
36
  include MonitorMixin
27
37
 
@@ -34,6 +44,11 @@ module Google
34
44
  # @return [Symbol]
35
45
  attr_accessor :action
36
46
 
47
+ ##
48
+ # Absolute path to the debuggee Ruby application root directory.
49
+ # @return [String]
50
+ attr_accessor :app_root
51
+
37
52
  ##
38
53
  # Only relevant when action is LOG. Defines the message to log when the
39
54
  # breakpoint hits. The message may include parameter placeholders $0,
@@ -124,8 +139,7 @@ module Google
124
139
  # field. The stored objects are nameless and get their name from the
125
140
  # referencing variable. The effective variable is a merge of the
126
141
  # referencing variable and the referenced variable.
127
- # TODO: Implement variable table
128
- # attr_accessor :variable_table
142
+ attr_accessor :variable_table
129
143
 
130
144
  ##
131
145
  # A set of custom breakpoint properties, populated by the agent, to be
@@ -145,6 +159,8 @@ module Google
145
159
 
146
160
  @id = id
147
161
  @action = :CAPTURE
162
+ # Use relative path for SourceLocation, because that's how the server
163
+ # side canonical breakpoints are defined.
148
164
  @location = SourceLocation.new.tap do |sl|
149
165
  sl.path = path
150
166
  sl.line = line.to_i
@@ -153,6 +169,7 @@ module Google
153
169
  @evaluated_expressions = []
154
170
  @stack_frames = []
155
171
  @labels = {}
172
+ @variable_table = VariableTable.new
156
173
  end
157
174
 
158
175
  ##
@@ -160,26 +177,39 @@ module Google
160
177
  # from a Google::Devtools::Clouddebugger::V2::Breakpoint object.
161
178
  def self.from_grpc grpc
162
179
  return new if grpc.nil?
163
- new.tap do |b|
180
+
181
+ breakpoint = grpc.action == :LOG ? Logpoint.new : Snappoint.new
182
+ breakpoint.tap do |b|
164
183
  b.id = grpc.id
165
184
  b.action = grpc.action
166
185
  b.condition = grpc.condition
167
- b.create_time = timestamp_from_grpc grpc.create_time
168
- b.evaluated_expressions =
169
- Breakpoint::Variable.from_grpc_list grpc.evaluated_expressions
170
186
  b.expressions = grpc.expressions.to_a
171
- b.final_time = timestamp_from_grpc grpc.final_time
172
187
  b.labels = hashify_labels grpc.labels
173
188
  b.log_message_format = grpc.log_message_format
174
189
  b.log_level = grpc.log_level
175
- b.location = Breakpoint::SourceLocation.from_grpc grpc.location
176
190
  b.is_final_state = grpc.is_final_state
177
- b.status = grpc.status
178
- b.stack_frames = stack_frames_from_grpc grpc
179
191
  b.user_email = grpc.user_email
192
+
193
+ assign_complex_grpc_fields grpc, b
180
194
  end
181
195
  end
182
196
 
197
+ ##
198
+ # @private Helper method that helps extracting complex fields from
199
+ # grpc struct into a breakpoint.
200
+ def self.assign_complex_grpc_fields grpc, breakpoint
201
+ breakpoint.create_time = timestamp_from_grpc grpc.create_time
202
+ breakpoint.evaluated_expressions =
203
+ Breakpoint::Variable.from_grpc_list grpc.evaluated_expressions
204
+ breakpoint.final_time = timestamp_from_grpc grpc.final_time
205
+ breakpoint.location =
206
+ Breakpoint::SourceLocation.from_grpc grpc.location
207
+ breakpoint.stack_frames = stack_frames_from_grpc grpc
208
+ breakpoint.status = Breakpoint::StatusMessage.from_grpc grpc.status
209
+ breakpoint.variable_table =
210
+ Breakpoint::VariableTable.from_grpc grpc.variable_table
211
+ end
212
+
183
213
  ##
184
214
  # @private Extract array of stack_frame from grpc
185
215
  def self.stack_frames_from_grpc grpc
@@ -205,8 +235,10 @@ module Google
205
235
  end
206
236
  end
207
237
 
208
- private_class_method :stack_frames_from_grpc, :timestamp_from_grpc,
209
- :hashify_labels
238
+ private_class_method :stack_frames_from_grpc,
239
+ :timestamp_from_grpc,
240
+ :hashify_labels,
241
+ :assign_complex_grpc_fields
210
242
 
211
243
  ##
212
244
  # Marks a breakpoint as complete if this breakpoint isn't completed
@@ -220,7 +252,21 @@ module Google
220
252
  end
221
253
  end
222
254
 
223
- alias_method :complete?, :is_final_state
255
+ ##
256
+ # Check if the breakpoint has been evaluated or set to a final error
257
+ # state.
258
+ def complete?
259
+ is_final_state ? true : false
260
+ end
261
+
262
+ ##
263
+ # Check if the breakpoint is valid or not. Invoke validation function
264
+ # if breakpoint hasn't been finallized yet.
265
+ def valid?
266
+ Validator.validate self unless complete?
267
+
268
+ status && status.is_error ? false : true
269
+ end
224
270
 
225
271
  ##
226
272
  # Get the file path of this breakpoint
@@ -244,44 +290,33 @@ module Google
244
290
 
245
291
  ##
246
292
  # Evaluate the breakpoint's condition expression against a given binding
247
- # object. Returns true if the condition expression evalutes to true;
248
- # false if error happens or the expression evaluates to false.
293
+ # object. Returns true if the condition expression evalutes to true or
294
+ # there isn't a condition; otherwise false. Set breakpoint to error
295
+ # state if exception happens.
249
296
  #
250
297
  # @param [Binding] binding A Ruby Binding object
251
- # @return [Boolean] True if condition evalutes to true, false if
252
- # condition evaluates to false or error raised during evaluation.
253
- #
298
+ # @return [Boolean] True if condition evalutes to true or there isn't a
299
+ # condition. False if condition evaluates to false or error raised
300
+ # during evaluation.
254
301
  def check_condition binding
255
302
  return true if condition.nil? || condition.empty?
256
- begin
257
- Evaluator.eval_condition binding, condition
258
- rescue
259
- set_error_state "Unable to evaluate condition",
260
- refers_to: :BREAKPOINT_CONDITION
261
- false
262
- end
263
- end
303
+ condition_result =
304
+ Evaluator.readonly_eval_expression binding, condition
264
305
 
265
- ##
266
- # Evaluate the breakpoint unless it's already marked as completed.
267
- # Store evaluted results in @evaluated_expressions, @stack_frames, and
268
- # @evaluated_log_message, depends on the action of breakpoint. Mark
269
- # breakpoint complete if successfully evaluated a breakpoint with
270
- # :CAPTURE action.
271
- #
272
- # @param [Array<Binding>] call_stack_bindings An array of Ruby Binding
273
- # objects, from the call stack that leads to the triggering of the
274
- # breakpoints.
275
- #
276
- # @return [Boolean] True if evaluated successfully; false otherwise.
277
- #
278
- def evaluate call_stack_bindings
279
- case action
280
- when :CAPTURE
281
- evaluate_snapshot_point call_stack_bindings
282
- when :LOG
283
- evaluate_logpoint call_stack_bindings[0]
306
+ if condition_result.is_a?(Exception) &&
307
+ condition_result.instance_variable_get(:@mutation_cause)
308
+ set_error_state "Error: #{condition_result.message}",
309
+ refers_to: StatusMessage::BREAKPOINT_CONDITION
310
+
311
+ return false
284
312
  end
313
+
314
+
315
+ condition_result ? true : false
316
+ rescue => e
317
+ set_error_state "Error: #{e.message}",
318
+ refers_to: StatusMessage::BREAKPOINT_CONDITION
319
+ false
285
320
  end
286
321
 
287
322
  ##
@@ -313,8 +348,9 @@ module Google
313
348
  user_email: user_email,
314
349
  stack_frames: stack_frames_to_grpc,
315
350
  evaluated_expressions: evaluated_expressions_to_grpc,
316
- status: status,
317
- labels: labels_to_grpc
351
+ status: status_to_grpc,
352
+ labels: labels_to_grpc,
353
+ variable_table: variable_table.to_grpc
318
354
  )
319
355
  end
320
356
 
@@ -324,79 +360,39 @@ module Google
324
360
  # completed if is_final is true.
325
361
  #
326
362
  # @param [String] message The error message
327
- # @param [Google::Devtools::Clouddebugger::V2::StatusMessage::Reference]
328
- # refers_to Enum that specifies what the error refers to. Defaults
329
- # :UNSPECIFIED.
363
+ # @param [Symbol] refers_to Enum that specifies what the error refers
364
+ # to. Defaults :UNSPECIFIED. See {Breakpoint::StatusMessage} class for
365
+ # list of possible values
330
366
  # @param [Boolean] is_final Marks the breakpoint as final if true.
331
367
  # Defaults true.
332
368
  #
333
- # @return [Google::Devtools::Clouddebugger::V2::StatusMessage] The grpc
369
+ # @return [Google::Cloud::Debugger::Breakpoint::StatusMessage] The grpc
334
370
  # StatusMessage object, which describes the breakpoint's error state.
335
- def set_error_state message, refers_to: :UNSPECIFIED, is_final: true
336
- description = Google::Devtools::Clouddebugger::V2::FormatMessage.new(
337
- format: message
338
- )
339
- @status = Google::Devtools::Clouddebugger::V2::StatusMessage.new(
340
- is_error: true,
341
- refers_to: refers_to,
342
- description: description
343
- )
371
+ def set_error_state message, refers_to: StatusMessage::UNSPECIFIED,
372
+ is_final: true
373
+ @status = StatusMessage.new.tap do |s|
374
+ s.is_error = true
375
+ s.refers_to = refers_to
376
+ s.description = message
377
+ end
344
378
 
345
379
  complete if is_final
346
380
 
347
381
  @status
348
382
  end
349
383
 
350
- private
351
-
352
384
  ##
353
- # @private Evaluate snapshot point
354
- def evaluate_snapshot_point call_stack_bindings
355
- synchronize do
356
- top_binding = call_stack_bindings[0]
357
-
358
- return false if complete? || !check_condition(top_binding)
359
-
360
- begin
361
- unless expressions.empty?
362
- @evaluated_expressions =
363
- Evaluator.eval_expressions top_binding, @expressions
364
- end
365
-
366
- @stack_frames = Evaluator.eval_call_stack call_stack_bindings
367
-
368
- complete
369
- rescue
370
- return false
371
- end
372
- end
373
-
374
- true
375
- end
376
-
377
- ##
378
- # @private Evaluate logpoint
379
- def evaluate_logpoint binding
380
- synchronize do
381
- return false if complete? || !check_condition(binding)
382
-
383
- begin
384
- unless expressions.empty?
385
- @evaluated_expressions = expressions.map do |expression|
386
- Evaluator.readonly_eval_expression binding, expression
387
- end
388
- end
389
- @evaluated_log_message =
390
- Evaluator.format_message log_message_format,
391
- evaluated_expressions
392
- rescue
393
- return false
394
- end
385
+ # Get full absolute file path by combining the relative file path
386
+ # with application root directory path.
387
+ def full_path
388
+ if app_root.nil? || app_root.empty?
389
+ path
390
+ else
391
+ File.join app_root, path
395
392
  end
396
-
397
- true
398
393
  end
399
394
 
395
+ private
400
396
 
401
397
  ##
402
398
  # @private Formats the labels so they can be saved to a
@@ -417,11 +413,18 @@ module Google
417
413
 
418
414
  ##
419
415
  # @private Exports the Breakpoint stack_frames to an array of
420
- # Google::Devtools::Clouddebugger::V2::StackFrame objects.
416
+ # Google::Devtools::Clouddebugger::V2::Variable objects.
421
417
  def evaluated_expressions_to_grpc
422
418
  evaluated_expressions.nil? ? [] : evaluated_expressions.map(&:to_grpc)
423
419
  end
424
420
 
421
+ ##
422
+ # @private Exports Breakpoint status to
423
+ # Google::Devtools::Clouddebugger::V2::StatusMessage object
424
+ def status_to_grpc
425
+ status.nil? ? nil: status.to_grpc
426
+ end
427
+
425
428
  ##
426
429
  # @private Formats the timestamp as a Google::Protobuf::Timestamp
427
430
  # object.