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 +4 -4
- data/ext/google/cloud/debugger/debugger_c/evaluator.c +8 -3
- data/ext/google/cloud/debugger/debugger_c/tracer.c +3 -13
- data/lib/google/cloud/debugger/agent.rb +21 -3
- data/lib/google/cloud/debugger/breakpoint.rb +110 -107
- data/lib/google/cloud/debugger/breakpoint/evaluator.rb +49 -162
- data/lib/google/cloud/debugger/breakpoint/status_message.rb +95 -0
- data/lib/google/cloud/debugger/breakpoint/validator.rb +91 -0
- data/lib/google/cloud/debugger/breakpoint/variable.rb +313 -41
- data/lib/google/cloud/debugger/breakpoint/variable_table.rb +96 -0
- data/lib/google/cloud/debugger/breakpoint_manager.rb +45 -10
- data/lib/google/cloud/debugger/credentials.rb +2 -1
- data/lib/google/cloud/debugger/logpoint.rb +97 -0
- data/lib/google/cloud/debugger/middleware.rb +16 -5
- data/lib/google/cloud/debugger/request_quota_manager.rb +95 -0
- data/lib/google/cloud/debugger/snappoint.rb +208 -0
- data/lib/google/cloud/debugger/tracer.rb +20 -32
- data/lib/google/cloud/debugger/version.rb +1 -1
- metadata +8 -2
@@ -0,0 +1,96 @@
|
|
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 "forwardable"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Debugger
|
21
|
+
class Breakpoint
|
22
|
+
##
|
23
|
+
# # VariableTable
|
24
|
+
#
|
25
|
+
# The variable_table exists to aid with computation, memory and network
|
26
|
+
# traffic optimization. It enables storing a variable once and reference
|
27
|
+
# it from multiple variables, including variables stored in the
|
28
|
+
# variable_table itself. For example, the same this object, which may
|
29
|
+
# appear at many levels of the stack, can have all of its data stored
|
30
|
+
# once in this table. The stack frame variables then would hold only a
|
31
|
+
# reference to it.
|
32
|
+
#
|
33
|
+
# The variable var_table_index field is an index into this repeated
|
34
|
+
# field. The stored objects are nameless and get their name from the
|
35
|
+
# referencing variable. The effective variable is a merge of the
|
36
|
+
# referencing variable and the referenced variable.
|
37
|
+
#
|
38
|
+
# See also {Breakpoint#variable_table}.
|
39
|
+
#
|
40
|
+
class VariableTable
|
41
|
+
extend Forwardable
|
42
|
+
|
43
|
+
##
|
44
|
+
# @private Array to store variables.
|
45
|
+
attr_accessor :variables
|
46
|
+
|
47
|
+
##
|
48
|
+
# @private Create a new VariableTable instance
|
49
|
+
def initialize
|
50
|
+
@variables = []
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# @private Create a new VariableTable instance from a variable table
|
55
|
+
# gRPC struct
|
56
|
+
def self.from_grpc grpc_table
|
57
|
+
return if grpc_table.nil?
|
58
|
+
|
59
|
+
new.tap do |vt|
|
60
|
+
vt.variables = grpc_table.map do |grpc_var|
|
61
|
+
Breakpoint::Variable.from_grpc grpc_var
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# @private Search a variable in this VariableTable by matching
|
68
|
+
# object_id, return the array index if found.
|
69
|
+
def rb_var_index rb_var
|
70
|
+
variables.each_with_index do |var, i|
|
71
|
+
return i if var.source_var.object_id == rb_var.object_id
|
72
|
+
end
|
73
|
+
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# @private Add a Breakpoint::Variable to this VariableTable
|
79
|
+
def add var
|
80
|
+
return unless var.is_a? Breakpoint::Variable
|
81
|
+
|
82
|
+
variables << var
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# @private Export this VariableTable as a gRPC struct
|
87
|
+
def to_grpc
|
88
|
+
variables.map(&:to_grpc).compact
|
89
|
+
end
|
90
|
+
|
91
|
+
def_instance_delegators :@variables, :size, :first, :[]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -13,7 +13,8 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
|
16
|
-
require "google/cloud/debugger/
|
16
|
+
require "google/cloud/debugger/snappoint"
|
17
|
+
require "google/cloud/debugger/logpoint"
|
17
18
|
|
18
19
|
module Google
|
19
20
|
module Cloud
|
@@ -92,10 +93,8 @@ module Google
|
|
92
93
|
|
93
94
|
@wait_token = response.next_wait_token
|
94
95
|
|
95
|
-
server_breakpoints =
|
96
|
-
|
97
|
-
Breakpoint.from_grpc grpc_b
|
98
|
-
end
|
96
|
+
server_breakpoints =
|
97
|
+
convert_grpc_breakpoints response.breakpoints || []
|
99
98
|
|
100
99
|
update_breakpoints server_breakpoints
|
101
100
|
|
@@ -117,9 +116,9 @@ module Google
|
|
117
116
|
def update_breakpoints server_breakpoints
|
118
117
|
synchronize do
|
119
118
|
new_breakpoints =
|
120
|
-
server_breakpoints -
|
121
|
-
|
122
|
-
|
119
|
+
filter_breakpoints server_breakpoints - breakpoints
|
120
|
+
|
121
|
+
before_breakpoints_count = breakpoints.size
|
123
122
|
|
124
123
|
# Remember new active breakpoints from server
|
125
124
|
@active_breakpoints += new_breakpoints unless new_breakpoints.empty?
|
@@ -127,8 +126,7 @@ module Google
|
|
127
126
|
# Forget old breakpoints
|
128
127
|
@completed_breakpoints &= server_breakpoints
|
129
128
|
@active_breakpoints &= server_breakpoints
|
130
|
-
after_breakpoints_acount =
|
131
|
-
@active_breakpoints.size + @completed_breakpoints.size
|
129
|
+
after_breakpoints_acount = breakpoints.size
|
132
130
|
|
133
131
|
breakpoints_updated =
|
134
132
|
!new_breakpoints.empty? ||
|
@@ -159,6 +157,7 @@ module Google
|
|
159
157
|
# Take this completed breakpoint off manager's active breakpoints
|
160
158
|
# list, submit the breakpoint snapshot, and update Tracer's
|
161
159
|
# breakpoints_cache.
|
160
|
+
|
162
161
|
return unless breakpoint.complete?
|
163
162
|
|
164
163
|
# Remove this breakpoint from active list
|
@@ -269,6 +268,42 @@ module Google
|
|
269
268
|
@completed_breakpoints.clear
|
270
269
|
end
|
271
270
|
end
|
271
|
+
|
272
|
+
private
|
273
|
+
|
274
|
+
##
|
275
|
+
# @private Convert the list of grpc breakpoints from Debugger service to
|
276
|
+
# {Google::Cloud::Debugger::Breakpoint}.
|
277
|
+
def convert_grpc_breakpoints grpc_breakpoints
|
278
|
+
grpc_breakpoints.map do |grpc_b|
|
279
|
+
breakpoint = Breakpoint.from_grpc grpc_b
|
280
|
+
breakpoint.app_root = agent.app_root
|
281
|
+
breakpoint.init_var_table if breakpoint.is_a? Debugger::Snappoint
|
282
|
+
breakpoint
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
##
|
287
|
+
# @private Varify a list of given breakpoints. Filter out those
|
288
|
+
# aren't valid and submit them directly.
|
289
|
+
def filter_breakpoints breakpoints
|
290
|
+
valid_breakpoints = []
|
291
|
+
invalid_breakpoints = []
|
292
|
+
|
293
|
+
breakpoints.each do |breakpoint|
|
294
|
+
if breakpoint.valid?
|
295
|
+
valid_breakpoints << breakpoint
|
296
|
+
else
|
297
|
+
invalid_breakpoints << breakpoint
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
invalid_breakpoints.each do |breakpoint|
|
302
|
+
agent.transmitter.submit breakpoint if breakpoint.complete?
|
303
|
+
end
|
304
|
+
|
305
|
+
valid_breakpoints
|
306
|
+
end
|
272
307
|
end
|
273
308
|
end
|
274
309
|
end
|
@@ -21,7 +21,8 @@ module Google
|
|
21
21
|
##
|
22
22
|
# @private Represents the OAuth 2.0 signing logic for Debugger.
|
23
23
|
class Credentials < Google::Cloud::Credentials
|
24
|
-
SCOPE = ["https://www.googleapis.com/auth/cloud_debugger"]
|
24
|
+
SCOPE = ["https://www.googleapis.com/auth/cloud_debugger"] +
|
25
|
+
Google::Cloud::Logging::Credentials::SCOPE
|
25
26
|
PATH_ENV_VARS = %w(DEBUGGER_KEYFILE GOOGLE_CLOUD_KEYFILE GCLOUD_KEYFILE)
|
26
27
|
JSON_ENV_VARS = %w(DEBUGGER_KEYFILE_JSON GOOGLE_CLOUD_KEYFILE_JSON
|
27
28
|
GCLOUD_KEYFILE_JSON)
|
@@ -0,0 +1,97 @@
|
|
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/breakpoint"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Debugger
|
21
|
+
##
|
22
|
+
# # Logpoint
|
23
|
+
#
|
24
|
+
# A kind of {Google::Cloud::Debugger::Breakpoint} that can be evaluated
|
25
|
+
# to generate a formatted log string, which later can be submitted to
|
26
|
+
# Stackdriver Logging service
|
27
|
+
#
|
28
|
+
class Logpoint < Breakpoint
|
29
|
+
##
|
30
|
+
# Evaluate the breakpoint unless it's already marked as completed.
|
31
|
+
# Store evaluted expressions and stack frame variables in
|
32
|
+
# @evaluated_expressions and @evaluated_log_message.
|
33
|
+
#
|
34
|
+
# @param [Array<Binding>] call_stack_bindings An array of Ruby Binding
|
35
|
+
# objects, from the call stack that leads to the triggering of the
|
36
|
+
# breakpoints.
|
37
|
+
#
|
38
|
+
# @return [Boolean] True if evaluated successfully; false otherwise.
|
39
|
+
#
|
40
|
+
def evaluate call_stack_bindings
|
41
|
+
synchronize do
|
42
|
+
binding = call_stack_bindings[0]
|
43
|
+
|
44
|
+
return false if complete? || !check_condition(binding)
|
45
|
+
|
46
|
+
begin
|
47
|
+
evaluate_log_message binding
|
48
|
+
rescue
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# @private Evaluate the expressions and log message. Store the result
|
58
|
+
# in @evaluated_log_message
|
59
|
+
def evaluate_log_message binding
|
60
|
+
evaluated_expressions = expressions.map do |expression|
|
61
|
+
Evaluator.readonly_eval_expression binding, expression
|
62
|
+
end
|
63
|
+
|
64
|
+
@evaluated_log_message =
|
65
|
+
format_message log_message_format, evaluated_expressions
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# @private Format log message by interpolate expressions.
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# Evaluator.format_log_message("Hello $0",
|
73
|
+
# ["World"]) #=> "Hello World"
|
74
|
+
#
|
75
|
+
# @param [String] message_format The message with with
|
76
|
+
# expression placeholders such as `$0`, `$1`, etc.
|
77
|
+
# @param [Array<Google::Cloud::Debugger::Breakpoint::Variable>]
|
78
|
+
# expressions An array of evaluated expression variables to be
|
79
|
+
# placed into message_format's placeholders. The variables need
|
80
|
+
# to have type equal String.
|
81
|
+
#
|
82
|
+
# @return [String] The formatted message string
|
83
|
+
#
|
84
|
+
def format_message message_format, expressions
|
85
|
+
# Substitute placeholders with expressions
|
86
|
+
message = message_format.gsub(/(?<!\$)\$\d+/) do |placeholder|
|
87
|
+
index = placeholder.match(/\$(\d+)/)[1].to_i
|
88
|
+
index < expressions.size ? expressions[index].inspect : ""
|
89
|
+
end
|
90
|
+
|
91
|
+
# Unescape "$" characters
|
92
|
+
message.gsub(/\$\$/, "$")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -14,6 +14,7 @@
|
|
14
14
|
|
15
15
|
|
16
16
|
require "google/cloud/logging/logger"
|
17
|
+
require "google/cloud/debugger/request_quota_manager"
|
17
18
|
|
18
19
|
module Google
|
19
20
|
module Cloud
|
@@ -44,11 +45,18 @@ module Google
|
|
44
45
|
|
45
46
|
load_config kwargs
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
if debugger
|
49
|
+
@debugger = debugger
|
50
|
+
else
|
51
|
+
@debugger =
|
52
|
+
Debugger.new(project: configuration.project_id,
|
53
|
+
keyfile: configuration.keyfile,
|
54
|
+
module_name: configuration.module_name,
|
55
|
+
module_version: configuration.module_version)
|
56
|
+
|
57
|
+
@debugger.agent.quota_manager =
|
58
|
+
Google::Cloud::Debugger::RequestQuotaManager.new
|
59
|
+
end
|
52
60
|
|
53
61
|
# Immediately start the debugger agent
|
54
62
|
@debugger.start
|
@@ -77,6 +85,9 @@ module Google
|
|
77
85
|
ensure
|
78
86
|
# Stop breakpoints tracing beyond this point
|
79
87
|
@debugger.agent.tracer.disable_traces_for_thread
|
88
|
+
|
89
|
+
# Reset quotas after each request finishes.
|
90
|
+
@debugger.agent.quota_manager.reset if @debugger.agent.quota_manager
|
80
91
|
end
|
81
92
|
|
82
93
|
private
|
@@ -0,0 +1,95 @@
|
|
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
|
+
module Google
|
17
|
+
module Cloud
|
18
|
+
module Debugger
|
19
|
+
##
|
20
|
+
# # RequestQuotaManager
|
21
|
+
#
|
22
|
+
# Tracking object used by debugger agent to manage quota in
|
23
|
+
# request-based applications. This class tracks the amount of time
|
24
|
+
# and number of breakpoints to evaluation in a single session.
|
25
|
+
#
|
26
|
+
# The debugger agent doesn't have use a quota manager by default, which
|
27
|
+
# means it will evaluate all breakpoints encountered and takes as much
|
28
|
+
# time as needed. This class is utilized by
|
29
|
+
# {Google::Cloud::Debugger::Middleware} class to limit latency overhead
|
30
|
+
# when used in Rack-based applications.
|
31
|
+
#
|
32
|
+
class RequestQuotaManager
|
33
|
+
# Default Total time allowed to consume, in seconds
|
34
|
+
DEFAULT_TIME_QUOTA = 0.05
|
35
|
+
|
36
|
+
# Default max number of breakpoints to evaluate
|
37
|
+
DEFAULT_COUNT_QUOTA = 10
|
38
|
+
|
39
|
+
##
|
40
|
+
# The time quota for this manager
|
41
|
+
attr_accessor :time_quota
|
42
|
+
|
43
|
+
##
|
44
|
+
# The count quota for this manager
|
45
|
+
attr_accessor :count_quota
|
46
|
+
|
47
|
+
##
|
48
|
+
# The time quota used
|
49
|
+
attr_accessor :time_used
|
50
|
+
|
51
|
+
##
|
52
|
+
# The count quota used
|
53
|
+
attr_accessor :count_used
|
54
|
+
|
55
|
+
##
|
56
|
+
# Construct a new RequestQuotaManager instance
|
57
|
+
#
|
58
|
+
# @param [Float] time_quota The max quota for time consumed.
|
59
|
+
# @param [Integer] count_quota The max quota for count usage.
|
60
|
+
def initialize time_quota: DEFAULT_TIME_QUOTA,
|
61
|
+
count_quota: DEFAULT_COUNT_QUOTA
|
62
|
+
@time_quota = time_quota
|
63
|
+
@time_used = 0
|
64
|
+
@count_quota = count_quota
|
65
|
+
@count_used = 0
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Check if there's more quota left.
|
70
|
+
#
|
71
|
+
# @return [Boolean] True if there's more quota; false otherwise.
|
72
|
+
def more?
|
73
|
+
(time_used < time_quota) && (count_used < count_quota)
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Reset all the quota usage.
|
78
|
+
def reset
|
79
|
+
@time_used = 0
|
80
|
+
@count_used = 0
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Notify the quota manager some resource has been consumed. Each time
|
85
|
+
# called increases the count quota usage.
|
86
|
+
#
|
87
|
+
# @param [Float] time Amount of time to deduct from the time quota.
|
88
|
+
def consume time: 0
|
89
|
+
@time_used += time
|
90
|
+
@count_used += 1
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,208 @@
|
|
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/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
|
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] binding 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
|