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.
- 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,74 @@
|
|
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
|
+
module Google
|
17
|
+
module Cloud
|
18
|
+
module Debugger
|
19
|
+
class Breakpoint
|
20
|
+
##
|
21
|
+
# # SourceLocation
|
22
|
+
#
|
23
|
+
# Additional information about the source code location that's
|
24
|
+
# associated with the breakpoint.
|
25
|
+
#
|
26
|
+
# See also {Breakpoint#location}.
|
27
|
+
#
|
28
|
+
class SourceLocation
|
29
|
+
##
|
30
|
+
# Path to the source file within the source context of the target
|
31
|
+
# binary.
|
32
|
+
attr_accessor :path
|
33
|
+
|
34
|
+
##
|
35
|
+
# Line inside the file. The first line in the file has the value 1.
|
36
|
+
attr_accessor :line
|
37
|
+
|
38
|
+
##
|
39
|
+
# @private Create an empty SourceLocation object.
|
40
|
+
def initialize; end
|
41
|
+
|
42
|
+
##
|
43
|
+
# @private New Google::Cloud::Debugger::Breakpoint::SourceLocation
|
44
|
+
# from a Google::Cloud::Debugger::V2::SourceLocation object.
|
45
|
+
def self.from_grpc grpc
|
46
|
+
return new if grpc.nil?
|
47
|
+
new.tap do |o|
|
48
|
+
o.path = grpc.path
|
49
|
+
o.line = grpc.line
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# @private Determines if the SourceLocation has any data.
|
55
|
+
def empty?
|
56
|
+
path.nil? &&
|
57
|
+
line.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# @private Exports the SourceLocation to a
|
62
|
+
# Google::Cloud::Debugger::V2::SourceLocation object.
|
63
|
+
def to_grpc
|
64
|
+
return nil if empty?
|
65
|
+
Google::Cloud::Debugger::V2::SourceLocation.new(
|
66
|
+
path: path.to_s,
|
67
|
+
line: line
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,109 @@
|
|
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/source_location"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Debugger
|
21
|
+
class Breakpoint
|
22
|
+
##
|
23
|
+
# # StackFrame
|
24
|
+
#
|
25
|
+
# Represents a stack frame context.
|
26
|
+
#
|
27
|
+
# See also {Breakpoint#stack_frames}.
|
28
|
+
#
|
29
|
+
class StackFrame
|
30
|
+
##
|
31
|
+
# Demangled function name at the call site.
|
32
|
+
attr_accessor :function
|
33
|
+
|
34
|
+
##
|
35
|
+
# Source location of the call site.
|
36
|
+
attr_accessor :location
|
37
|
+
|
38
|
+
##
|
39
|
+
# Set of arguments passed to this function. Note that this might not
|
40
|
+
# be populated for all stack frames.
|
41
|
+
attr_accessor :arguments
|
42
|
+
|
43
|
+
##
|
44
|
+
# Set of local variables at the stack frame location. Note that this
|
45
|
+
# might not be populated for all stack frames.
|
46
|
+
attr_accessor :locals
|
47
|
+
|
48
|
+
##
|
49
|
+
# @private Create an empty StackFrame object.
|
50
|
+
def initialize
|
51
|
+
@location = SourceLocation.new
|
52
|
+
@arguments = []
|
53
|
+
@locals = []
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# @private New Google::Cloud::Debugger::Breakpoint::SourceLocation
|
58
|
+
# from a Google::Cloud::Debugger::V2::SourceLocation object.
|
59
|
+
def self.from_grpc grpc
|
60
|
+
new.tap do |o|
|
61
|
+
o.function = grpc.function
|
62
|
+
o.location = SourceLocation.from_grpc grpc.location
|
63
|
+
o.arguments = Variable.from_grpc_list grpc.arguments
|
64
|
+
o.locals = Variable.from_grpc_list grpc.locals
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# @private Determines if the StackFrame has any data.
|
70
|
+
def empty?
|
71
|
+
function.nil? &&
|
72
|
+
location.nil? &&
|
73
|
+
arguments.nil? &&
|
74
|
+
locals.nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# @private Exports the StackFrame to a
|
79
|
+
# Google::Cloud::Debugger::V2::StackFrame object.
|
80
|
+
def to_grpc
|
81
|
+
return nil if empty?
|
82
|
+
Google::Cloud::Debugger::V2::StackFrame.new(
|
83
|
+
function: function.to_s,
|
84
|
+
location: location.to_grpc,
|
85
|
+
arguments: arguments_to_grpc,
|
86
|
+
locals: locals_to_grpc
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
##
|
93
|
+
# @private Exports the StackFrame arguments to an array of
|
94
|
+
# Google::Cloud::Debugger::V2::Variable objects.
|
95
|
+
def arguments_to_grpc
|
96
|
+
arguments.nil? ? [] : arguments.map(&:to_grpc)
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# @private Exports the StackFrame locals to an array of
|
101
|
+
# Google::Cloud::Debugger::V2::Variable objects.
|
102
|
+
def locals_to_grpc
|
103
|
+
locals.nil? ? [] : locals.map(&:to_grpc)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,93 @@
|
|
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
|
+
module Google
|
17
|
+
module Cloud
|
18
|
+
module Debugger
|
19
|
+
class Breakpoint
|
20
|
+
##
|
21
|
+
# # StatusMessage
|
22
|
+
#
|
23
|
+
# Represents a contextual status message. The message can indicate an
|
24
|
+
# error or informational status, and refer to specific parts of the
|
25
|
+
# containing object. For example, the Breakpoint.status field can
|
26
|
+
# indicate an error referring to the BREAKPOINT_SOURCE_LOCATION with
|
27
|
+
# the message Location not found.
|
28
|
+
class StatusMessage
|
29
|
+
##
|
30
|
+
# Constants used as references to which the message applies.
|
31
|
+
UNSPECIFIED = :UNSPECIFIED
|
32
|
+
BREAKPOINT_SOURCE_LOCATION = :BREAKPOINT_SOURCE_LOCATION
|
33
|
+
BREAKPOINT_CONDITION = :BREAKPOINT_CONDITION
|
34
|
+
BREAKPOINT_EXPRESSION = :BREAKPOINT_EXPRESSION
|
35
|
+
BREAKPOINT_AGE = :BREAKPOINT_AGE
|
36
|
+
VARIABLE_NAME = :VARIABLE_NAME
|
37
|
+
VARIABLE_VALUE = :VARIABLE_VALUE
|
38
|
+
|
39
|
+
##
|
40
|
+
# Distinguishes errors from informational messages.
|
41
|
+
attr_accessor :is_error
|
42
|
+
|
43
|
+
##
|
44
|
+
# Reference to which the message applies.
|
45
|
+
attr_accessor :refers_to
|
46
|
+
|
47
|
+
##
|
48
|
+
# Status message text.
|
49
|
+
attr_accessor :description
|
50
|
+
|
51
|
+
##
|
52
|
+
# New Google::Cloud::Debugger::Breakpoint::StatusMessage
|
53
|
+
# from a Google::Cloud::Debugger::V2::StatusMessage object.
|
54
|
+
def self.from_grpc grpc
|
55
|
+
return nil if grpc.nil?
|
56
|
+
new.tap do |s|
|
57
|
+
s.is_error = grpc.is_error
|
58
|
+
s.refers_to = grpc.refers_to
|
59
|
+
s.description = grpc.description.format
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# @private Construct a new StatusMessage instance.
|
65
|
+
def initialize
|
66
|
+
@refers_to = UNSPECIFIED
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# @private Determines if the Variable has any data.
|
71
|
+
def empty?
|
72
|
+
is_error.nil? &&
|
73
|
+
refers_to.nil? &&
|
74
|
+
description.nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Exports the StatusMessage to a
|
79
|
+
# Google::Cloud::Debugger::V2::StatusMessage object.
|
80
|
+
def to_grpc
|
81
|
+
return nil if empty?
|
82
|
+
description_grpc = Google::Cloud::Debugger::V2::FormatMessage.new format: description.to_s
|
83
|
+
|
84
|
+
Google::Cloud::Debugger::V2::StatusMessage.new \
|
85
|
+
is_error: true,
|
86
|
+
refers_to: refers_to,
|
87
|
+
description: description_grpc
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,92 @@
|
|
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
|
+
module Google
|
17
|
+
module Cloud
|
18
|
+
module Debugger
|
19
|
+
class Breakpoint
|
20
|
+
##
|
21
|
+
# # Validator
|
22
|
+
#
|
23
|
+
# A collection of static methods to help validate a given breakpoint.
|
24
|
+
module Validator
|
25
|
+
FILE_NOT_FOUND_MSG = "File not found.".freeze
|
26
|
+
WRONG_FILE_TYPE_MSG = "File must be a `.rb` file.".freeze
|
27
|
+
INVALID_LINE_MSG = "Invalid line.".freeze
|
28
|
+
|
29
|
+
##
|
30
|
+
# Validate a given breakpoint. Set breakpoint to error state if
|
31
|
+
# the breakpoint fails validation.
|
32
|
+
def self.validate breakpoint
|
33
|
+
error_msg = nil
|
34
|
+
if !verify_file_path breakpoint.full_path
|
35
|
+
error_msg = FILE_NOT_FOUND_MSG
|
36
|
+
elsif !verify_file_type breakpoint.full_path
|
37
|
+
error_msg = WRONG_FILE_TYPE_MSG
|
38
|
+
elsif !verify_line breakpoint.full_path, breakpoint.line
|
39
|
+
error_msg = INVALID_LINE_MSG
|
40
|
+
end
|
41
|
+
|
42
|
+
if error_msg
|
43
|
+
cause = Breakpoint::StatusMessage::BREAKPOINT_SOURCE_LOCATION
|
44
|
+
breakpoint.set_error_state error_msg, refers_to: cause
|
45
|
+
false
|
46
|
+
else
|
47
|
+
true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# @private Verifies the given file path exists
|
53
|
+
def self.verify_file_path file_path
|
54
|
+
File.exist? file_path
|
55
|
+
rescue StandardError
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# @private Check the file from given file path is a Ruby file
|
61
|
+
def self.verify_file_type file_path
|
62
|
+
File.extname(file_path) == ".rb" ||
|
63
|
+
File.basename(file_path) == "config.ru"
|
64
|
+
rescue StandardError
|
65
|
+
false
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# @private Verifies the given line from a Ruby
|
70
|
+
def self.verify_line file_path, line
|
71
|
+
file = File.open file_path, "r"
|
72
|
+
|
73
|
+
# Skip through lines from beginning
|
74
|
+
file.gets while file.lineno < line - 1 && !file.eof?
|
75
|
+
|
76
|
+
line = file.gets
|
77
|
+
|
78
|
+
# Make sure we have a line (not eof)
|
79
|
+
return false unless line
|
80
|
+
|
81
|
+
blank_line = line =~ /^\s*$/
|
82
|
+
comment_line = line =~ /^\s*#.*$/
|
83
|
+
|
84
|
+
blank_line || comment_line ? false : true
|
85
|
+
rescue StandardError
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,595 @@
|
|
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/status_message"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Debugger
|
21
|
+
class Breakpoint
|
22
|
+
##
|
23
|
+
# # Variable
|
24
|
+
#
|
25
|
+
# Represents a variable or an argument possibly of a compound object
|
26
|
+
# type. Note how the following variables are represented:
|
27
|
+
#
|
28
|
+
# A simple Variable:
|
29
|
+
# ```ruby
|
30
|
+
# x = 5
|
31
|
+
# # Captured variable:
|
32
|
+
# # { name: "x", value: "5", type: "Integer" }
|
33
|
+
# ```
|
34
|
+
#
|
35
|
+
# A Compound Variable:
|
36
|
+
# ```ruby
|
37
|
+
# class T
|
38
|
+
# attr_accessor :m1, :m2
|
39
|
+
# ...
|
40
|
+
# end
|
41
|
+
# v = T.new(1, "2")
|
42
|
+
# # Captured variable:
|
43
|
+
# # {
|
44
|
+
# # name: "v",
|
45
|
+
# # type: "T",
|
46
|
+
# # members: [
|
47
|
+
# # { name: "@m1", value: "1", type: "Integer" },
|
48
|
+
# # { name: "@m2", value: "2", type: "String" }
|
49
|
+
# # ]
|
50
|
+
# # }
|
51
|
+
# ```
|
52
|
+
#
|
53
|
+
# A Hash object:
|
54
|
+
# ```ruby
|
55
|
+
# hash = { a: 1, b: :two }
|
56
|
+
# # Captured variable:
|
57
|
+
# # {
|
58
|
+
# # name: "hash",
|
59
|
+
# # type: "Hash",
|
60
|
+
# # members: [
|
61
|
+
# # { name: "a", value: "1", type: "Integer" },
|
62
|
+
# # { name: "b", value: ":2", type: "Symbol" }
|
63
|
+
# # ]
|
64
|
+
# # }
|
65
|
+
# ```
|
66
|
+
#
|
67
|
+
# An Array object:
|
68
|
+
# ```ruby
|
69
|
+
# ary = [1, nil]
|
70
|
+
# # Captured variable:
|
71
|
+
# # {
|
72
|
+
# # name: "ary",
|
73
|
+
# # type: "Array",
|
74
|
+
# # members: [
|
75
|
+
# # { name: "[0]", value: "1", type: "Integer" },
|
76
|
+
# # { name: "[1]", value: "nil", type: "NilClass" }
|
77
|
+
# # ]
|
78
|
+
# # }
|
79
|
+
# ```
|
80
|
+
#
|
81
|
+
class Variable
|
82
|
+
##
|
83
|
+
# Max depth to convert on compound variables
|
84
|
+
MAX_DEPTH = 3
|
85
|
+
|
86
|
+
##
|
87
|
+
# Max number of member variables to evaluate in compound variables
|
88
|
+
MAX_MEMBERS = 1000
|
89
|
+
|
90
|
+
##
|
91
|
+
# Max length on variable inspect results. Truncate extra and replace
|
92
|
+
# with ellipsis.
|
93
|
+
MAX_STRING_LENGTH = 500
|
94
|
+
|
95
|
+
##
|
96
|
+
# @private Minimum amount of size limit needed for evaluation.
|
97
|
+
MIN_REQUIRED_SIZE = 100
|
98
|
+
|
99
|
+
##
|
100
|
+
# @private Message to display on variables when snapshot buffer is
|
101
|
+
# full.
|
102
|
+
BUFFER_FULL_MSG =
|
103
|
+
"Buffer full. Use an expression to see more data.".freeze
|
104
|
+
|
105
|
+
##
|
106
|
+
# @private Error message when variable can't be converted.
|
107
|
+
FAIL_CONVERSION_MSG = "Error: Unable to inspect value".freeze
|
108
|
+
|
109
|
+
##
|
110
|
+
# @private Name of the variable, if any.
|
111
|
+
# @return [String]
|
112
|
+
attr_accessor :name
|
113
|
+
|
114
|
+
##
|
115
|
+
# @private Simple value of the variable.
|
116
|
+
# @return [String]
|
117
|
+
attr_accessor :value
|
118
|
+
|
119
|
+
##
|
120
|
+
# @private Variable type (e.g. MyClass). If the variable is split with
|
121
|
+
# var_table_index, type goes next to value.
|
122
|
+
# @return [String]
|
123
|
+
attr_accessor :type
|
124
|
+
|
125
|
+
##
|
126
|
+
# @private Members contained or pointed to by the variable.
|
127
|
+
# @return [Array<Variable>]
|
128
|
+
attr_accessor :members
|
129
|
+
|
130
|
+
##
|
131
|
+
# @private Reference to a variable in the shared variable table. More
|
132
|
+
# than one variable can reference the same variable in the table. The
|
133
|
+
# var_table_index field is an index into variable_table in Breakpoint.
|
134
|
+
attr_accessor :var_table_index
|
135
|
+
|
136
|
+
##
|
137
|
+
# @private The variable table this variable references to (if
|
138
|
+
# var_table_index is set).
|
139
|
+
attr_accessor :var_table
|
140
|
+
|
141
|
+
##
|
142
|
+
# @private Status associated with the variable. This field will
|
143
|
+
# usually stay unset. A status of a single variable only applies to
|
144
|
+
# that variable or expression. The rest of breakpoint data still
|
145
|
+
# remains valid. Variables might be reported in error state even when
|
146
|
+
# breakpoint is not in final state.
|
147
|
+
# The message may refer to variable name with refers_to set to
|
148
|
+
# VARIABLE_NAME. Alternatively refers_to will be set to
|
149
|
+
# VARIABLE_VALUE. In either case variable value and members will be
|
150
|
+
# unset.
|
151
|
+
attr_accessor :status
|
152
|
+
|
153
|
+
##
|
154
|
+
# @private The original Ruby object this Breakpoint::Variable is based
|
155
|
+
# upon.
|
156
|
+
attr_accessor :source_var
|
157
|
+
|
158
|
+
##
|
159
|
+
# @private Create an empty Variable object.
|
160
|
+
def initialize
|
161
|
+
@members = []
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Convert a Ruby variable into a
|
166
|
+
# Google::Cloud::Debugger::Breakpoint::Variable object. If
|
167
|
+
# a variable table is provided, it will store all the subsequently
|
168
|
+
# created compound variables into the variable table for sharing.
|
169
|
+
#
|
170
|
+
# @param [Any] source Source Ruby variable to convert from
|
171
|
+
# @param [String] name Name of the varaible
|
172
|
+
# @param [Integer] depth Number of levels to evaluate in compound
|
173
|
+
# variables. Default to
|
174
|
+
# {Google::Cloud::Debugger::Breakpoint::Variable::MAX_DEPTH}
|
175
|
+
# @param [Breakpoint::VariableTable] var_table A variable table
|
176
|
+
# to store shared compound variables. Optional.
|
177
|
+
# @param [Integer] limit Maximum number of bytes this conversion
|
178
|
+
# should take. This include nested compound member variables'
|
179
|
+
# conversions.
|
180
|
+
#
|
181
|
+
# @example Simple variable conversion
|
182
|
+
# x = 3.0
|
183
|
+
# var = Google::Cloud::Debugger::Breakpoint::Variable.from_rb_var \
|
184
|
+
# x, name: "x"
|
185
|
+
# var.name #=> "x"
|
186
|
+
# var.value #=> "3.0"
|
187
|
+
# var.type #=> "Float"
|
188
|
+
#
|
189
|
+
# @example Hash conversion
|
190
|
+
# hash = {a: 1.0, b: :two}
|
191
|
+
# var = Google::Cloud::Debugger::Breakpoint::Variable.from_rb_var \
|
192
|
+
# hash, name: "hash"
|
193
|
+
# var.name #=> "hash"
|
194
|
+
# var.type #=> "Hash"
|
195
|
+
# var.members[0].name #=> "a"
|
196
|
+
# var.members[0].value #=> "1.0"
|
197
|
+
# var.members[0].type #=> "Float"
|
198
|
+
# var.members[1].name #=> "b"
|
199
|
+
# var.members[1].value #=> ":two"
|
200
|
+
# var.members[1].type #=> "Symbol"
|
201
|
+
#
|
202
|
+
# @example Custom compound variable conversion
|
203
|
+
# foo = Foo.new(a: 1.0, b: [])
|
204
|
+
# foo.inspect #=> "#<Foo:0xXXXXXX @a=1.0, @b=[]>"
|
205
|
+
# var = Google::Cloud::Debugger::Breakpoint::Variable.from_rb_var \
|
206
|
+
# foo, name: "foo"
|
207
|
+
# var.name #=> "foo"
|
208
|
+
# var.type #=> "Foo"
|
209
|
+
# var.members[0].name #=> "@a"
|
210
|
+
# var.members[0].value #=> "1.0"
|
211
|
+
# var.members[0].type #=> "Float"
|
212
|
+
# var.members[1].name #=> "@b"
|
213
|
+
# var.members[1].value #=> "[]"
|
214
|
+
# var.members[1].type #=> "Array"
|
215
|
+
#
|
216
|
+
# @example Use variable table for shared compound variables
|
217
|
+
# hash = {a: 1.0}
|
218
|
+
# ary = [hash, hash]
|
219
|
+
# var_table = Google::Cloud::Debugger::Breakpoint::VariableTable.new
|
220
|
+
# var = Google::Cloud::Debugger::Breakpoint::Variable.from_rb_var \
|
221
|
+
# ary, name: "ary", var_table: var_table
|
222
|
+
# var.name #=> "ary"
|
223
|
+
# var.var_table_index #=> 0
|
224
|
+
# var_table[0].type #=> "Array"
|
225
|
+
# var_table[0].members[0].name #=> "[0]"
|
226
|
+
# var_table[0].members[0].var_table_index #=> 1
|
227
|
+
# var_table[0].members[1].name #=> "[1]"
|
228
|
+
# var_table[0].members[1].var_table_index #=> 1
|
229
|
+
# var_table[1].type #=> "Hash"
|
230
|
+
# var_table[1].members[0].name #=> "a"
|
231
|
+
# var_table[1].members[0].type #=> "Float"
|
232
|
+
# var_table[1].members[0].value #=> "1.0"
|
233
|
+
#
|
234
|
+
# @return [Google::Cloud::Debugger::Breakpoint::Variable] Converted
|
235
|
+
# variable.
|
236
|
+
#
|
237
|
+
def self.from_rb_var source, name: nil, depth: MAX_DEPTH,
|
238
|
+
var_table: nil, limit: nil
|
239
|
+
return source if source.is_a? Variable
|
240
|
+
|
241
|
+
if limit && limit < MIN_REQUIRED_SIZE
|
242
|
+
return buffer_full_variable var_table
|
243
|
+
end
|
244
|
+
|
245
|
+
# If source is a non-empty Array or Hash, or source has instance
|
246
|
+
# variables, evaluate source as a compound variable.
|
247
|
+
if compound_var?(source) && depth > 0
|
248
|
+
from_compound_var source, name: name, depth: depth,
|
249
|
+
var_table: var_table, limit: limit
|
250
|
+
else
|
251
|
+
from_primitive_var source, name: name, limit: limit
|
252
|
+
end
|
253
|
+
rescue StandardError
|
254
|
+
new.tap do |var|
|
255
|
+
var.name = name.to_s if name
|
256
|
+
var.set_error_state FAIL_CONVERSION_MSG
|
257
|
+
var.source_var = source
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
##
|
262
|
+
# @private Helper method that converts primitive variables.
|
263
|
+
def self.from_primitive_var source, name: nil, limit: nil
|
264
|
+
new.tap do |var|
|
265
|
+
var.name = name.to_s if name
|
266
|
+
var.type = source.class.to_s
|
267
|
+
var.source_var = source
|
268
|
+
limit = deduct_limit limit,
|
269
|
+
var.name.to_s.bytesize + var.type.bytesize
|
270
|
+
|
271
|
+
var.value = truncate_value source.inspect, limit
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
##
|
276
|
+
# @private Helper method that converts compound variables.
|
277
|
+
def self.from_compound_var source, name: nil, depth: MAX_DEPTH,
|
278
|
+
var_table: nil, limit: nil
|
279
|
+
return source if source.is_a? Variable
|
280
|
+
|
281
|
+
if limit && limit < MIN_REQUIRED_SIZE
|
282
|
+
return buffer_full_variable var_table
|
283
|
+
end
|
284
|
+
|
285
|
+
var = new
|
286
|
+
var.name = name.to_s if name
|
287
|
+
var.source_var = source
|
288
|
+
limit = deduct_limit limit, var.name.to_s.bytesize
|
289
|
+
|
290
|
+
if var_table
|
291
|
+
var.var_table = var_table
|
292
|
+
var.var_table_index =
|
293
|
+
var_table.rb_var_index(source) ||
|
294
|
+
add_shared_compound_var(source, depth, var_table, limit: limit)
|
295
|
+
else
|
296
|
+
var.type = source.class.to_s
|
297
|
+
limit = deduct_limit limit, var.type.bytesize
|
298
|
+
add_compound_members var, source, depth, limit: limit
|
299
|
+
end
|
300
|
+
var
|
301
|
+
end
|
302
|
+
|
303
|
+
##
|
304
|
+
# @private Determine if a given Ruby variable is a compound variable.
|
305
|
+
def self.compound_var? source
|
306
|
+
((source.is_a?(Hash) || source.is_a?(Array)) && !source.empty?) ||
|
307
|
+
!source.instance_variables.empty?
|
308
|
+
end
|
309
|
+
|
310
|
+
##
|
311
|
+
# @private Add a shared compound variable to the breakpoint
|
312
|
+
# variable table.
|
313
|
+
def self.add_shared_compound_var source, depth, var_table, limit: nil
|
314
|
+
var = new
|
315
|
+
var.type = source.class.to_s
|
316
|
+
var.source_var = source
|
317
|
+
limit = deduct_limit limit, var.type.bytesize
|
318
|
+
|
319
|
+
table_index = var_table.size
|
320
|
+
var_table.add var
|
321
|
+
|
322
|
+
add_compound_members var, source, depth, var_table, limit: limit
|
323
|
+
|
324
|
+
table_index
|
325
|
+
end
|
326
|
+
|
327
|
+
##
|
328
|
+
# @private Add member variables to a compound variable.
|
329
|
+
def self.add_compound_members var, source, depth, var_table = nil,
|
330
|
+
limit: nil
|
331
|
+
case source
|
332
|
+
when Hash
|
333
|
+
add_member_vars var, source, limit: limit do |(k, v), _, lmt|
|
334
|
+
from_rb_var v, name: k, depth: depth - 1, var_table: var_table,
|
335
|
+
limit: lmt
|
336
|
+
end
|
337
|
+
when Array
|
338
|
+
add_member_vars var, source, limit: limit do |el, i, lmt|
|
339
|
+
from_rb_var el, name: "[#{i}]", depth: depth - 1,
|
340
|
+
var_table: var_table, limit: lmt
|
341
|
+
end
|
342
|
+
else
|
343
|
+
members = source.instance_variables
|
344
|
+
add_member_vars var, members, limit: limit do |var_name, _, lmt|
|
345
|
+
instance_var = source.instance_variable_get var_name
|
346
|
+
from_rb_var instance_var, name: var_name, depth: depth - 1,
|
347
|
+
var_table: var_table, limit: lmt
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
##
|
353
|
+
# @private Help interate through collection of member variables for
|
354
|
+
# compound variables.
|
355
|
+
def self.add_member_vars var, members, limit: nil
|
356
|
+
members.each_with_index do |member, i|
|
357
|
+
member_var = yield member, i, limit
|
358
|
+
|
359
|
+
limit = deduct_limit limit, member_var.total_size
|
360
|
+
|
361
|
+
buffer_full = (limit && limit < 0) ||
|
362
|
+
i >= MAX_MEMBERS ||
|
363
|
+
member_var.buffer_full_variable?
|
364
|
+
|
365
|
+
if buffer_full
|
366
|
+
var.members << Variable.new.tap do |last_var|
|
367
|
+
last_var.set_error_state \
|
368
|
+
"Only first #{i} items were captured. Use in " \
|
369
|
+
"an expression to see all items."
|
370
|
+
end
|
371
|
+
break
|
372
|
+
else
|
373
|
+
var.members << member_var
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
##
|
379
|
+
# @private Create an empty variable that points to the shared
|
380
|
+
# "Buffer Full" variable in the given variable table (always index 0)
|
381
|
+
# if a variable table is passed in. Otherwise create an error variable
|
382
|
+
# with the buffer full message.
|
383
|
+
def self.buffer_full_variable var_table = nil, name: nil
|
384
|
+
new.tap do |var|
|
385
|
+
var.name = name if name
|
386
|
+
|
387
|
+
if var_table && var_table.first &&
|
388
|
+
var_table.first.buffer_full_variable?
|
389
|
+
var.var_table = var_table
|
390
|
+
var.var_table_index = 0
|
391
|
+
else
|
392
|
+
var.set_error_state BUFFER_FULL_MSG
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
##
|
398
|
+
# @private Helper method to calculate bytesize limit deduction.
|
399
|
+
def self.deduct_limit limit, used
|
400
|
+
limit.nil? ? nil : limit - used
|
401
|
+
end
|
402
|
+
|
403
|
+
private_class_method :add_compound_members,
|
404
|
+
:add_shared_compound_var,
|
405
|
+
:add_member_vars,
|
406
|
+
:compound_var?,
|
407
|
+
:deduct_limit
|
408
|
+
|
409
|
+
##
|
410
|
+
# @private New Google::Cloud::Debugger::Breakpoint::Variable
|
411
|
+
# from a Google::Cloud::Debugger::V2::Variable object.
|
412
|
+
def self.from_grpc grpc
|
413
|
+
return new if grpc.nil?
|
414
|
+
new.tap do |o|
|
415
|
+
o.name = grpc.name
|
416
|
+
o.value = grpc.value
|
417
|
+
o.type = grpc.type
|
418
|
+
o.members = from_grpc_list grpc.members
|
419
|
+
o.var_table_index = var_table_index_from_grpc grpc.var_table_index
|
420
|
+
o.status = Breakpoint::StatusMessage.from_grpc grpc.status
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
##
|
425
|
+
# @private New array of Google::Cloud::Debugger::Breakpoint::Variable
|
426
|
+
# from an array of Google::Cloud::Debugger::V2::Variable
|
427
|
+
# objects.
|
428
|
+
def self.from_grpc_list grpc_list
|
429
|
+
return [] if grpc_list.nil?
|
430
|
+
grpc_list.map { |var_grpc| from_grpc var_grpc }
|
431
|
+
end
|
432
|
+
|
433
|
+
##
|
434
|
+
# @private Extract var_table_index from the equivalent GRPC struct
|
435
|
+
def self.var_table_index_from_grpc grpc
|
436
|
+
grpc.nil? ? nil : grpc.value
|
437
|
+
end
|
438
|
+
|
439
|
+
##
|
440
|
+
# @private Limit string to MAX_STRING_LENTH. Replace extra characters
|
441
|
+
# with ellipsis
|
442
|
+
def self.truncate_value str, limit = nil
|
443
|
+
limit ||= MAX_STRING_LENGTH
|
444
|
+
str.gsub(/(.{#{limit - 3}}).+/, '\1...')
|
445
|
+
end
|
446
|
+
private_class_method :add_compound_members, :truncate_value
|
447
|
+
|
448
|
+
##
|
449
|
+
# @private Determines if the Variable has any data.
|
450
|
+
def empty?
|
451
|
+
name.nil? &&
|
452
|
+
value.nil? &&
|
453
|
+
type.nil? &&
|
454
|
+
members.nil? &&
|
455
|
+
var_table_index.nil? &&
|
456
|
+
status.nil?
|
457
|
+
end
|
458
|
+
|
459
|
+
##
|
460
|
+
# Exports the Variable to a
|
461
|
+
# Google::Cloud::Debugger::V2::Variable object.
|
462
|
+
def to_grpc
|
463
|
+
return nil if empty?
|
464
|
+
Google::Cloud::Debugger::V2::Variable.new(
|
465
|
+
name: name.to_s,
|
466
|
+
value: value.to_s,
|
467
|
+
type: type.to_s,
|
468
|
+
var_table_index: var_table_index_to_grpc,
|
469
|
+
members: members_to_grpc || [],
|
470
|
+
status: status_to_grpc
|
471
|
+
)
|
472
|
+
end
|
473
|
+
|
474
|
+
##
|
475
|
+
# Set this variable to an error state by setting the status field
|
476
|
+
def set_error_state message, refers_to: StatusMessage::VARIABLE_VALUE
|
477
|
+
@status = StatusMessage.new.tap do |s|
|
478
|
+
s.is_error = true
|
479
|
+
s.refers_to = refers_to
|
480
|
+
s.description = message
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
##
|
485
|
+
# Calculate the total bytesize of all the attributes and that of the
|
486
|
+
# member variables, plus references into other variables in the
|
487
|
+
# variable table.
|
488
|
+
#
|
489
|
+
# @return [Integer] The total payload size of this variable in bytes.
|
490
|
+
def total_size
|
491
|
+
unless @total_size
|
492
|
+
vars = [self, *(unique_members || [])]
|
493
|
+
|
494
|
+
@total_size = vars.inject payload_size do |sum, var|
|
495
|
+
if var.var_table && var.var_table_index
|
496
|
+
sum + var.var_table[var.var_table_index].total_size
|
497
|
+
else
|
498
|
+
sum
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
@total_size
|
504
|
+
end
|
505
|
+
|
506
|
+
##
|
507
|
+
# Calculate the bytesize of all the attributes and that of the
|
508
|
+
# member variables.
|
509
|
+
#
|
510
|
+
# @return [Integer] The total payload size of this variable in bytes.
|
511
|
+
def payload_size
|
512
|
+
unless @payload_size
|
513
|
+
@payload_size = name.to_s.bytesize +
|
514
|
+
type.to_s.bytesize +
|
515
|
+
value.to_s.bytesize
|
516
|
+
|
517
|
+
unless members.nil?
|
518
|
+
@payload_size = members.inject @payload_size do |sum, member|
|
519
|
+
sum + member.payload_size
|
520
|
+
end
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
@payload_size
|
525
|
+
end
|
526
|
+
|
527
|
+
##
|
528
|
+
# @private Get a unique array of members that don't reference
|
529
|
+
# same object in variable table
|
530
|
+
def unique_members
|
531
|
+
seen_indices = {}
|
532
|
+
|
533
|
+
members.select do |member|
|
534
|
+
if seen_indices[member.var_table_index]
|
535
|
+
false
|
536
|
+
else
|
537
|
+
seen_indices[member.var_table_index] = true
|
538
|
+
true
|
539
|
+
end
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
##
|
544
|
+
# @private Whether this variable is a reference variable into
|
545
|
+
# the shared variable table or not.
|
546
|
+
def reference_variable?
|
547
|
+
value.nil? && members.empty? && !var_table_index.nil?
|
548
|
+
end
|
549
|
+
|
550
|
+
##
|
551
|
+
# @private Check if a given variable is a buffer full variable, or an
|
552
|
+
# reference variable to the shared buffer full variable
|
553
|
+
def buffer_full_variable?
|
554
|
+
if (status &&
|
555
|
+
status.description == BUFFER_FULL_MSG) ||
|
556
|
+
(var_table &&
|
557
|
+
reference_variable? &&
|
558
|
+
var_table_index.zero? &&
|
559
|
+
var_table[0] &&
|
560
|
+
var_table[0].status &&
|
561
|
+
var_table[0].status.description == BUFFER_FULL_MSG)
|
562
|
+
true
|
563
|
+
else
|
564
|
+
false
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
private
|
569
|
+
|
570
|
+
##
|
571
|
+
# @private Exports the Variable status to grpc
|
572
|
+
def status_to_grpc
|
573
|
+
status.nil? ? nil : status.to_grpc
|
574
|
+
end
|
575
|
+
|
576
|
+
##
|
577
|
+
# @private Exports the Variable var_table_index attribute to
|
578
|
+
# an Int32Value gRPC struct
|
579
|
+
def var_table_index_to_grpc
|
580
|
+
return unless var_table_index
|
581
|
+
|
582
|
+
Google::Protobuf::Int32Value.new value: var_table_index
|
583
|
+
end
|
584
|
+
|
585
|
+
##
|
586
|
+
# @private Exports the Variable members to an array of
|
587
|
+
# Google::Cloud::Debugger::V2::Variable objects.
|
588
|
+
def members_to_grpc
|
589
|
+
members.nil? ? nil : members.map(&:to_grpc)
|
590
|
+
end
|
591
|
+
end
|
592
|
+
end
|
593
|
+
end
|
594
|
+
end
|
595
|
+
end
|