google-cloud-debugger 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
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,78 @@
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 "pathname"
17
+ require "set"
18
+
19
+ module Google
20
+ module Cloud
21
+ module Debugger
22
+ class Debuggee
23
+ ##
24
+ # Generates an application debuggee uniquifier by hashing the
25
+ # application file stats.
26
+ #
27
+ module AppUniquifierGenerator
28
+ ##
29
+ # Max number of directory levels the generator looks into.
30
+ MAX_DEPTH = 10
31
+
32
+ ##
33
+ # Computes the application uniquifier by examine the file stats of
34
+ # the files in the given application root directory. It only looks at
35
+ # .rb files and Gemfile.lock
36
+ #
37
+ # @param [Digest::SHA] sha A digest SHA object used to add the hashing
38
+ # values
39
+ # @param [String] app_path Application root directory where the Ruby
40
+ # application is located.
41
+ #
42
+ # @return [NilClass]
43
+ #
44
+ def self.generate_app_uniquifier sha, app_path = nil
45
+ app_path ||= if defined?(::Rack::Directory)
46
+ Rack::Directory.new("").root
47
+ else
48
+ Dir.getwd
49
+ end
50
+
51
+ process_directory sha, app_path
52
+
53
+ nil
54
+ end
55
+
56
+ private
57
+
58
+ ##
59
+ # @private Recursively parse files and directories.
60
+ def self.process_directory sha, path, depth = 1
61
+ return if depth > MAX_DEPTH
62
+
63
+ pathname = Pathname.new path
64
+ pathname.each_child do |entry|
65
+ if entry.directory?
66
+ process_directory sha, entry, depth + 1
67
+ else
68
+ next unless entry.extname == ".rb" ||
69
+ entry.basename.to_s == "Gemfile.lock"
70
+ sha << "#{entry.expand_path}:#{entry.stat.size}"
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,77 @@
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
+ # Rack Middleware implementation that supports Stackdriver Debugger Agent
21
+ # in Rack-based Ruby frameworks. It instantiates a new debugger agent if
22
+ # one isn't given already. It helps optimize Debugger Agent Tracer
23
+ # performance by suspend and resume tracer between each request.
24
+ class Middleware
25
+ ##
26
+ # Create a new Debugger Middleware.
27
+ #
28
+ # @param [Rack Application] app Rack application
29
+ # @param [Google::Cloud::Debugger::Project] debugger A debugger to be
30
+ # used by this middleware. If not given, will construct a new one
31
+ # using the other parameters.
32
+ # @param [String] project Project identifier for the Stackdriver
33
+ # Debugger service. Optional if a debugger is given.
34
+ # @param [String, Hash] keyfile Keyfile downloaded from Google Cloud:
35
+ # either the JSON data or the path to a readable file. Optional if
36
+ # a debugger is given.
37
+ # @param [String] module_name Name for the debuggee application.
38
+ # Optional if a debugger is given.
39
+ # @param [String] module_version Version identifier for the debuggee
40
+ # application. Optiona if a debugger is given.
41
+ #
42
+ # @return [Google::Cloud::Debugger::Middleware] A new
43
+ # Google::Cloud::Debugger::Middleware instance
44
+ #
45
+ def initialize app, debugger: nil, module_name:nil, module_version: nil,
46
+ project: nil, keyfile: nil
47
+ @app = app
48
+ @debugger = debugger || Debugger.new(project: project,
49
+ keyfile: keyfile,
50
+ module_name: module_name,
51
+ module_version: module_version)
52
+ @debugger.start
53
+ end
54
+
55
+ ##
56
+ # Rack middleware entry point. In most Rack based frameworks, a request
57
+ # is served by one thread. It enables/resume the debugger breakpoints
58
+ # tracing and stops/pauses the tracing afterwards to help improve
59
+ # debugger performance.
60
+ #
61
+ # @param [Hash] env Rack environment hash
62
+ #
63
+ # @return [Rack::Response] The response from downstream Rack app
64
+ #
65
+ def call env
66
+ # Enable/resume breakpoints tracing
67
+ @debugger.agent.tracer.start
68
+
69
+ @app.call env
70
+ ensure
71
+ # Stop breakpoints tracing beyond this point
72
+ @debugger.agent.tracer.disable_traces_for_thread
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,135 @@
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/errors"
17
+ require "google/cloud/env"
18
+ require "google/cloud/debugger/agent"
19
+ require "google/cloud/debugger/credentials"
20
+ require "google/cloud/debugger/middleware"
21
+ require "google/cloud/debugger/service"
22
+
23
+ module Google
24
+ module Cloud
25
+ module Debugger
26
+ ##
27
+ # # Project
28
+ #
29
+ # Projects are top-level containers in Google Cloud Platform. They store
30
+ # information about billing and authorized users, and they control access
31
+ # to Stackdriver Debugger resources. Each project has a friendly name and
32
+ # a unique ID. Projects can be created only in the [Google Developers
33
+ # Console](https://console.developers.google.com).
34
+ #
35
+ # @example
36
+ # require "google/cloud/debugger"
37
+ #
38
+ # debugger = Google::Cloud::Debugger.new
39
+ # debugger.start
40
+ #
41
+ # See Google::Cloud#debugger
42
+ class Project
43
+ ##
44
+ # @private The gRPC Service object.
45
+ attr_accessor :service
46
+
47
+ ##
48
+ # The Stackdriver Debugger Agent object.
49
+ attr_reader :agent
50
+
51
+ ##
52
+ # @private Creates a new Project instance.
53
+ def initialize service, module_name:, module_version:
54
+ @service = service
55
+ @agent = Agent.new service, module_name: module_name,
56
+ module_version: module_version
57
+ end
58
+
59
+ ##
60
+ # The ID of the current project.
61
+ #
62
+ # @return [String] the Google Cloud project ID
63
+ #
64
+ # @example
65
+ # require "google/cloud/debugger"
66
+ #
67
+ # debugger = Google::Cloud::Debugger.new(
68
+ # project: "my-project",
69
+ # keyfile: "/path/to/keyfile.json"
70
+ # )
71
+ #
72
+ # debugger.project #=> "my-project"
73
+ #
74
+ def project
75
+ service.project
76
+ end
77
+
78
+ ##
79
+ # @private Default project.
80
+ def self.default_project
81
+ ENV["DEBUGGER_PROJECT"] ||
82
+ ENV["GOOGLE_CLOUD_PROJECT"] ||
83
+ ENV["GCLOUD_PROJECT"] ||
84
+ Google::Cloud.env.project_id
85
+ end
86
+
87
+ ##
88
+ # @private Default module name identifier.
89
+ def self.default_module_name
90
+ ENV["DEBUGGER_MODULE_NAME"] ||
91
+ Google::Cloud.env.app_engine_service_id ||
92
+ "ruby-app"
93
+ end
94
+
95
+ ##
96
+ # @private Default module version identifier.
97
+ def self.default_module_version
98
+ ENV["DEBUGGER_MODULE_VERSION"] ||
99
+ Google::Cloud.env.app_engine_service_version ||
100
+ ""
101
+ end
102
+
103
+ ##
104
+ # Start the Stackdriver Debugger Agent.
105
+ #
106
+ # @example
107
+ # require "google/cloud/debugger"
108
+ #
109
+ # debugger = Google::Cloud::Debugger.new
110
+ # debugger.start
111
+ #
112
+ # See {Agent#start} for more details.
113
+ def start
114
+ agent.start
115
+ end
116
+ alias_method :attach, :start
117
+
118
+ ##
119
+ # Stop the Stackdriver Debugger Agent.
120
+ #
121
+ # @example
122
+ # require "google/cloud/debugger"
123
+ #
124
+ # debugger = Google::Cloud::Debugger.new
125
+ # debugger.start
126
+ # debugger.stop
127
+ #
128
+ # See {Agent#stop} for more details.
129
+ def stop
130
+ agent.stop
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,141 @@
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 "google/cloud/debugger"
16
+
17
+ module Google
18
+ module Cloud
19
+ module Debugger
20
+ ##
21
+ # # Railtie
22
+ #
23
+ # Google::Cloud::Debugger::Railtie automatically adds the
24
+ # Google::Cloud::Debugger::Middleware to Rack in a Rails environment.
25
+ #
26
+ # The Middleware is only added when certain conditions are met. See
27
+ # {Railtie.use_debugger?} for detail.
28
+ #
29
+ # When loaded, the Google::Cloud::Debugger::Middleware will be inserted
30
+ # after the Rack::ETag Middleware, which is top of the Rack stack, closest
31
+ # to the application code.
32
+ #
33
+ # The Railtie should also initialize a debugger to be used by the
34
+ # middleware. The debugger can be configured using the following Rails
35
+ # configuration:
36
+ # @example
37
+ # # Explicitly enable or disable Stackdriver Debugger Agent
38
+ # config.google_cloud.use_debugger = true
39
+ # # Shared Google Cloud Platform project identifier
40
+ # config.google_cloud.project_id = "gcloud-project"
41
+ # # Google Cloud Platform project identifier for Stackdriver Debugger
42
+ # config.google_cloud.debugger.project_id = "debugger-project"
43
+ # # Share Google Cloud authentication json file
44
+ # config.google_cloud.keyfile = "/path/to/keyfile.json"
45
+ # # Google Cloud authentication json file for Stackdriver Debugger only
46
+ # config.google_cloud.debugger.keyfile = "/path/to/keyfile.json"
47
+ # # Stackdriver Debugger Agent module name identifier
48
+ # config.google_cloud.debugger.module_name = "my-ruby-app"
49
+ # # Stackdriver Debugger Agent module version identifier
50
+ # config.google_cloud.debugger.module_version = "v1"
51
+ #
52
+ class Railtie < ::Rails::Railtie
53
+ config.google_cloud = ::ActiveSupport::OrderedOptions.new unless
54
+ config.respond_to? :google_cloud
55
+ config.google_cloud[:debugger] = ::ActiveSupport::OrderedOptions.new
56
+ config.google_cloud.define_singleton_method :debugger do
57
+ self[:debugger]
58
+ end
59
+
60
+ initializer "Stackdriver.Debugger" do |app|
61
+ debugger_config = Railtie.parse_rails_config config
62
+
63
+ project_id = debugger_config[:project_id]
64
+ keyfile = debugger_config[:keyfile]
65
+ module_name = debugger_config[:module_name]
66
+ module_version = debugger_config[:module_version]
67
+
68
+ debugger = Google::Cloud::Debugger.new project: project_id,
69
+ keyfile: keyfile,
70
+ module_name: module_name,
71
+ module_version: module_version
72
+
73
+ if self.class.use_debugger? app.config
74
+ app.middleware.insert_after Rack::ETag,
75
+ Google::Cloud::Debugger::Middleware,
76
+ debugger: debugger
77
+ end
78
+ end
79
+
80
+ ##
81
+ # Determine whether to use Stackdriver Debugger or not.
82
+ #
83
+ # Returns true if valid GCP project_id is provided and underneath API is
84
+ # able to authenticate. Also either Rails needs to be in "production"
85
+ # environment or config.stackdriver.use_debugger is explicitly true.
86
+ #
87
+ # @param [Rails::Railtie::Configuration] config The
88
+ # Rails.application.config
89
+ #
90
+ # @return [Boolean] Whether to use Stackdriver Debugger
91
+ #
92
+ def self.use_debugger? config
93
+ debugger_config = parse_rails_config config
94
+
95
+ # Return false if config.stackdriver.use_debugger is explicitly false
96
+ use_debugger = debugger_config[:use_debugger]
97
+ return false if !use_debugger.nil? && !use_debugger
98
+
99
+ # Try authenticate authorize client API. Return false if unable to
100
+ # authorize.
101
+ begin
102
+ Google::Cloud::Debugger::Credentials.credentials_with_scope(
103
+ debugger_config[:keyfile])
104
+ rescue => e
105
+ Rails.log "Google::Cloud::Debugger is not activated due to " \
106
+ "authorization error: #{e.message}"
107
+ return false
108
+ end
109
+
110
+ project_id = debugger_config[:project_id] ||
111
+ Google::Cloud::Debugger::Project.default_project
112
+ if project_id.to_s.empty?
113
+ Rails.log "Google::Cloud::Debugger is not activated due to empty " \
114
+ "project_id"
115
+ return false
116
+ end
117
+
118
+ # Otherwise default to true if Rails is running in production or
119
+ # config.stackdriver.use_debugger is true
120
+ Rails.env.production? || use_debugger
121
+ end
122
+
123
+ ##
124
+ # @private Helper method to parse rails config into a flattened hash
125
+ def self.parse_rails_config config
126
+ gcp_config = config.google_cloud
127
+ debugger_config = gcp_config[:debugger]
128
+ use_debugger =
129
+ gcp_config.key?(:use_debugger) ? gcp_config.use_debugger : nil
130
+ {
131
+ project_id: debugger_config.project_id || gcp_config.project_id,
132
+ keyfile: debugger_config.keyfile || gcp_config.keyfile,
133
+ module_name: debugger_config.module_name,
134
+ module_version: debugger_config.module_version,
135
+ use_debugger: use_debugger
136
+ }
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,130 @@
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/errors"
17
+ require "google/cloud/debugger/version"
18
+ require "google/cloud/debugger/v2"
19
+ require "google/gax/errors"
20
+
21
+ module Google
22
+ module Cloud
23
+ module Debugger
24
+ ##
25
+ # @private Represents the gRPC Debugger service, including all the API
26
+ # methods.
27
+ class Service
28
+ attr_accessor :project, :credentials, :host, :timeout, :client_config
29
+
30
+ ##
31
+ # Creates a new Service instance.
32
+ def initialize project, credentials, host: nil, timeout: nil,
33
+ client_config: nil
34
+ @project = project
35
+ @credentials = credentials
36
+ @host = host || V2::Controller2Client::SERVICE_ADDRESS
37
+ @timeout = timeout
38
+ @client_config = client_config || {}
39
+ end
40
+
41
+ def channel
42
+ require "grpc"
43
+ GRPC::Core::Channel.new host, nil, chan_creds
44
+ end
45
+
46
+ def chan_creds
47
+ return credentials if insecure?
48
+ require "grpc"
49
+ GRPC::Core::ChannelCredentials.new.compose \
50
+ GRPC::Core::CallCredentials.new credentials.client.updater_proc
51
+ end
52
+
53
+ def cloud_debugger
54
+ return mocked_debugger if mocked_debugger
55
+ @debugger ||=
56
+ V2::Controller2Client.new(
57
+ service_path: host,
58
+ channel: channel,
59
+ timeout: timeout,
60
+ client_config: client_config,
61
+ lib_name: "gccl",
62
+ lib_version: Google::Cloud::Debugger::VERSION)
63
+ end
64
+ attr_accessor :mocked_debugger
65
+
66
+ def transmitter
67
+ return mocked_transmitter if mocked_transmitter
68
+ @transmitter ||=
69
+ V2::Controller2Client.new(
70
+ service_path: host,
71
+ channel: channel,
72
+ timeout: timeout,
73
+ client_config: client_config,
74
+ lib_name: "gccl",
75
+ lib_version: Google::Cloud::Debugger::VERSION)
76
+ end
77
+ attr_accessor :mocked_transmitter
78
+
79
+ def insecure?
80
+ credentials == :this_channel_is_insecure
81
+ end
82
+
83
+ def register_debuggee debuggee_grpc
84
+ execute do
85
+ cloud_debugger.register_debuggee debuggee_grpc,
86
+ options: default_options
87
+ end
88
+ end
89
+
90
+ def list_active_breakpoints debuggee_id, wait_token
91
+ execute do
92
+ cloud_debugger.list_active_breakpoints debuggee_id.to_s,
93
+ wait_token: wait_token.to_s,
94
+ success_on_timeout: true,
95
+ options: default_options
96
+ end
97
+ end
98
+
99
+ def update_active_breakpoint debuggee_id, breakpoint
100
+ execute do
101
+ transmitter.update_active_breakpoint debuggee_id.to_s,
102
+ breakpoint.to_grpc,
103
+ options: default_options
104
+ end
105
+ end
106
+
107
+ def inspect
108
+ "#{self.class}(#{@project})"
109
+ end
110
+
111
+ protected
112
+
113
+ def default_headers
114
+ { "google-cloud-resource-prefix" => "projects/#{@project}" }
115
+ end
116
+
117
+ def default_options
118
+ Google::Gax::CallOptions.new kwargs: default_headers
119
+ end
120
+
121
+ def execute
122
+ yield
123
+ rescue Google::Gax::GaxError => e
124
+ # GaxError wraps BadStatus, but exposes it as #cause
125
+ raise Google::Cloud::Error.from_error(e.cause)
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end