app_profiler 0.1.1 → 0.1.2
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/lib/app_profiler/middleware/upload_action.rb +3 -2
- data/lib/app_profiler/railtie.rb +1 -0
- data/lib/app_profiler/server.rb +90 -37
- data/lib/app_profiler/version.rb +1 -1
- data/lib/app_profiler.rb +4 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5e9b631f4b4b68fb8a2f6f2882ee0e4259cf6151d37388b299bd55c5a0ac6c3
|
4
|
+
data.tar.gz: a31ef0b50de69b81c84d5d8ec411e82db94a9aa2690fe633239e6c01f2bd2165
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4829d7292b8cc680c6936763b8fed26c5ab8f8661ada68f6c3b6a1890cee01d3ad7fb01bf377a3ce4cd3083f8b3b554d5b048c74ac594c540096724083adfb37
|
7
|
+
data.tar.gz: 20798219dc410c7b2ef0096e437ba8e7d8793a3dabac2a66246a765c8db06fca195c5d05981cff4adb28c6df40456876174e5f1d2b0c292fdff4f9922a1c059a
|
@@ -27,8 +27,9 @@ module AppProfiler
|
|
27
27
|
return unless autoredirect
|
28
28
|
|
29
29
|
# Automatically redirect to profile if autoredirect is true.
|
30
|
-
|
31
|
-
|
30
|
+
location = AppProfiler.profile_url(upload)
|
31
|
+
if response[0].to_i < 500 && location
|
32
|
+
response[1]["Location"] = location
|
32
33
|
response[0] = 303
|
33
34
|
end
|
34
35
|
end
|
data/lib/app_profiler/railtie.rb
CHANGED
@@ -5,6 +5,7 @@ require "rails"
|
|
5
5
|
module AppProfiler
|
6
6
|
class Railtie < Rails::Railtie
|
7
7
|
config.app_profiler = ActiveSupport::OrderedOptions.new
|
8
|
+
config.app_profiler.profile_url_formatter = DefaultProfileFormatter
|
8
9
|
|
9
10
|
initializer "app_profiler.configs" do |app|
|
10
11
|
AppProfiler.logger = app.config.app_profiler.logger || Rails.logger
|
data/lib/app_profiler/server.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
require "socket"
|
4
4
|
require "rack"
|
5
5
|
require "tempfile"
|
6
|
+
require "json"
|
7
|
+
require "active_support/core_ext/hash"
|
8
|
+
require "active_support/core_ext/module"
|
6
9
|
|
7
10
|
# This module provides a means to start a golang-inspired profile server
|
8
11
|
# it is implemented using stdlib and Rack to avoid additional dependencies
|
@@ -49,80 +52,122 @@ module AppProfiler
|
|
49
52
|
private
|
50
53
|
|
51
54
|
def handle(request)
|
52
|
-
|
53
|
-
|
54
|
-
response.status = HTTP_NOT_ALLOWED
|
55
|
-
response.write("Only GET requests are supported")
|
56
|
-
return response
|
57
|
-
end
|
55
|
+
return handle_not_allowed(request) if request.request_method != "GET"
|
56
|
+
|
58
57
|
case request.path
|
59
58
|
when "/profile"
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
return response
|
66
|
-
end
|
59
|
+
handle_profile(request)
|
60
|
+
else
|
61
|
+
handle_not_found(request)
|
62
|
+
end
|
63
|
+
end
|
67
64
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
65
|
+
def handle_not_allowed(request)
|
66
|
+
response = Rack::Response.new
|
67
|
+
|
68
|
+
response.status = HTTP_NOT_ALLOWED
|
69
|
+
response.write("Only GET requests are supported")
|
70
|
+
|
71
|
+
response
|
72
|
+
end
|
73
|
+
|
74
|
+
def handle_profile(request)
|
75
|
+
begin
|
76
|
+
stackprof_args, duration = validate_profile_params(request.params)
|
77
|
+
rescue InvalidProfileArgsError => e
|
78
|
+
return handle_bad_request(request, e.message)
|
79
|
+
end
|
80
|
+
|
81
|
+
response = Rack::Response.new
|
82
|
+
|
83
|
+
if start_running(stackprof_args)
|
84
|
+
start_time = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
|
85
|
+
|
86
|
+
sleep(duration)
|
87
|
+
|
88
|
+
profile = stop_running
|
89
|
+
|
90
|
+
response.status = HTTP_OK
|
91
|
+
response.set_header("Content-Type", "application/json")
|
92
|
+
|
93
|
+
profile_hash = profile.to_h
|
94
|
+
profile_hash["start_time_nsecs"] = start_time # NOTE: this is not part of the stackprof profile spec
|
95
|
+
|
96
|
+
response.write(JSON.dump(profile_hash))
|
97
|
+
|
98
|
+
if AppProfiler::Server.cors
|
99
|
+
response.set_header("Access-Control-Allow-Origin", AppProfiler::Server.cors_host)
|
85
100
|
end
|
86
101
|
else
|
87
|
-
response.status =
|
88
|
-
response.write("
|
102
|
+
response.status = HTTP_CONFLICT
|
103
|
+
response.write("A profile is already running")
|
89
104
|
end
|
105
|
+
|
106
|
+
response
|
107
|
+
end
|
108
|
+
|
109
|
+
def handle_not_found(request)
|
110
|
+
response = Rack::Response.new
|
111
|
+
|
112
|
+
response.status = HTTP_NOT_FOUND
|
113
|
+
response.write("Unsupported endpoint #{request.path}")
|
114
|
+
|
115
|
+
response
|
116
|
+
end
|
117
|
+
|
118
|
+
def handle_bad_request(request, message)
|
119
|
+
response = Rack::Response.new
|
120
|
+
|
121
|
+
response.status = HTTP_BAD_REQUEST
|
122
|
+
response.write("Invalid argument #{message}")
|
123
|
+
|
90
124
|
response
|
91
125
|
end
|
92
126
|
|
93
127
|
def validate_profile_params(params)
|
94
128
|
params = params.symbolize_keys
|
95
129
|
stackprof_args = {}
|
130
|
+
|
96
131
|
begin
|
97
132
|
duration = Float(params.key?(:duration) ? params[:duration] : AppProfiler::Server.duration)
|
98
133
|
rescue ArgumentError
|
99
|
-
raise InvalidProfileArgsError, "
|
134
|
+
raise InvalidProfileArgsError, "duration: #{params[:duration]}"
|
100
135
|
end
|
136
|
+
|
101
137
|
if params.key?(:mode)
|
102
138
|
if ["cpu", "wall", "object"].include?(params[:mode])
|
103
139
|
stackprof_args[:mode] = params[:mode].to_sym
|
104
140
|
else
|
105
|
-
raise InvalidProfileArgsError, "
|
141
|
+
raise InvalidProfileArgsError, "mode: #{params[:mode]}"
|
106
142
|
end
|
107
143
|
end
|
144
|
+
|
108
145
|
if params.key?(:interval)
|
109
146
|
stackprof_args[:interval] = params[:interval].to_i
|
110
|
-
|
147
|
+
|
148
|
+
raise InvalidProfileArgsError, "interval: #{params[:interval]}" if stackprof_args[:interval] <= 0
|
111
149
|
end
|
150
|
+
|
112
151
|
[stackprof_args, duration]
|
113
152
|
end
|
114
153
|
|
115
154
|
# Prevent multiple concurrent profiles by synchronizing between threads
|
116
|
-
def start_running
|
155
|
+
def start_running(stackprof_args)
|
117
156
|
@semaphore.synchronize do
|
118
157
|
return false if @profile_running
|
119
158
|
|
120
159
|
@profile_running = true
|
160
|
+
|
161
|
+
AppProfiler.start(**stackprof_args)
|
121
162
|
end
|
122
163
|
end
|
123
164
|
|
124
165
|
def stop_running
|
125
|
-
@semaphore.synchronize
|
166
|
+
@semaphore.synchronize do
|
167
|
+
AppProfiler.stop.tap do
|
168
|
+
@profile_running = false
|
169
|
+
end
|
170
|
+
end
|
126
171
|
end
|
127
172
|
end
|
128
173
|
|
@@ -147,6 +192,7 @@ module AppProfiler
|
|
147
192
|
class UNIX < Transport
|
148
193
|
def initialize
|
149
194
|
super
|
195
|
+
|
150
196
|
FileUtils.mkdir_p(PROFILER_TEMPFILE_PATH)
|
151
197
|
@socket_file = File.join(PROFILER_TEMPFILE_PATH, "app-profiler-#{Process.pid}.sock")
|
152
198
|
File.unlink(@socket_file) if File.exist?(@socket_file) && File.socket?(@socket_file)
|
@@ -216,10 +262,13 @@ module AppProfiler
|
|
216
262
|
loop do
|
217
263
|
Thread.new(@transport.socket.accept) do |session|
|
218
264
|
request = session.gets
|
265
|
+
|
219
266
|
if request.nil?
|
220
267
|
session.close
|
268
|
+
|
221
269
|
next
|
222
270
|
end
|
271
|
+
|
223
272
|
method, path, http_version = request.split(" ")
|
224
273
|
path_info, query_string = path.split("?")
|
225
274
|
env = { # an extremely minimal rack env hash, just enough to get the job done
|
@@ -233,10 +282,13 @@ module AppProfiler
|
|
233
282
|
|
234
283
|
begin
|
235
284
|
session.print("#{http_version} #{status}\r\n")
|
285
|
+
|
236
286
|
headers.each do |header, value|
|
237
287
|
session.print("#{header}: #{value}\r\n")
|
238
288
|
end
|
289
|
+
|
239
290
|
session.print("\r\n")
|
291
|
+
|
240
292
|
body.each do |part|
|
241
293
|
session.print(part)
|
242
294
|
end
|
@@ -265,6 +317,7 @@ module AppProfiler
|
|
265
317
|
class << self
|
266
318
|
def reset
|
267
319
|
profile_servers.clear
|
320
|
+
|
268
321
|
DEFAULTS.each do |config, value|
|
269
322
|
class_variable_set(:"@@#{config}", value) # rubocop:disable Style/ClassVars
|
270
323
|
end
|
data/lib/app_profiler/version.rb
CHANGED
data/lib/app_profiler.rb
CHANGED
@@ -4,7 +4,6 @@ require "active_support/core_ext/class"
|
|
4
4
|
require "active_support/core_ext/module"
|
5
5
|
require "logger"
|
6
6
|
require "app_profiler/version"
|
7
|
-
require "app_profiler/railtie" if defined?(Rails::Railtie)
|
8
7
|
|
9
8
|
module AppProfiler
|
10
9
|
class ConfigurationError < StandardError
|
@@ -81,7 +80,11 @@ module AppProfiler
|
|
81
80
|
end
|
82
81
|
|
83
82
|
def profile_url(upload)
|
83
|
+
return unless AppProfiler.profile_url_formatter
|
84
|
+
|
84
85
|
AppProfiler.profile_url_formatter.call(upload)
|
85
86
|
end
|
86
87
|
end
|
88
|
+
|
89
|
+
require "app_profiler/railtie" if defined?(Rails::Railtie)
|
87
90
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: app_profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gannon McGibbon
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2022-
|
16
|
+
date: 2022-09-23 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: activesupport
|