mini-sharp-hub 0.0.1
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/mini-sharp-hub.gemspec +12 -0
- data/rack-mini-profiler-4.0.1/CHANGELOG.md +424 -0
- data/rack-mini-profiler-4.0.1/README.md +542 -0
- data/rack-mini-profiler-4.0.1/lib/enable_rails_patches.rb +5 -0
- data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/USAGE +9 -0
- data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/install_generator.rb +13 -0
- data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/templates/rack_mini_profiler.rb +8 -0
- data/rack-mini-profiler-4.0.1/lib/generators/rack_profiler/USAGE +2 -0
- data/rack-mini-profiler-4.0.1/lib/generators/rack_profiler/install_generator.rb +16 -0
- data/rack-mini-profiler-4.0.1/lib/html/dot.1.1.2.min.js +2 -0
- data/rack-mini-profiler-4.0.1/lib/html/includes.css +474 -0
- data/rack-mini-profiler-4.0.1/lib/html/includes.js +1520 -0
- data/rack-mini-profiler-4.0.1/lib/html/includes.scss +698 -0
- data/rack-mini-profiler-4.0.1/lib/html/includes.tmpl +333 -0
- data/rack-mini-profiler-4.0.1/lib/html/pretty-print.js +810 -0
- data/rack-mini-profiler-4.0.1/lib/html/profile_handler.js +1 -0
- data/rack-mini-profiler-4.0.1/lib/html/rack-mini-profiler.css +3 -0
- data/rack-mini-profiler-4.0.1/lib/html/rack-mini-profiler.js +2 -0
- data/rack-mini-profiler-4.0.1/lib/html/share.html +10 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/LICENSE +21 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/README.md +3 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/demangle-cpp.1768f4cc.js +4 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/favicon-16x16.f74b3187.png +0 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/favicon-32x32.bc503437.png +0 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/file-format-schema.json +324 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-regular.css +8 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-v13-regular.woff +0 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-v13-regular.woff2 +0 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/import.8ae8aa3d.js +123 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/index.html +2 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/release.txt +3 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/reset.8c46b7a1.css +2 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/source-map.438fa06b.js +24 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/speedscope.f27db165.js +193 -0
- data/rack-mini-profiler-4.0.1/lib/html/vendor.js +848 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/actions.rb +157 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/asset_version.rb +6 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/client_settings.rb +127 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/config.rb +146 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/context.rb +14 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/gc_profiler.rb +158 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/profiling_methods.rb +167 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/snapshots_transporter.rb +111 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/abstract_store.rb +97 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/file_store.rb +179 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/memcache_store.rb +105 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/memory_store.rb +233 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/redis_store.rb +353 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage.rb +7 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/base.rb +39 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/client.rb +90 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/custom.rb +25 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/page.rb +146 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/request.rb +186 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/sql.rb +89 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct.rb +8 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/version.rb +8 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/views.rb +198 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler.rb +670 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler_rails/railtie.rb +234 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler_rails/railtie_methods.rb +61 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/activerecord.rb +44 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/mongo.rb +18 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/moped.rb +14 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/alias_method.rb +30 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/prepend.rb +34 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2.rb +7 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/neo4j.rb +16 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/nobrainer.rb +31 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/oracle_enhanced.rb +70 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb +121 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/pg/prepend.rb +115 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/pg.rb +7 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/plucky.rb +49 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/riak.rb +105 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/rsolr.rb +26 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/sequel.rb +12 -0
- data/rack-mini-profiler-4.0.1/lib/patches/net_patches.rb +27 -0
- data/rack-mini-profiler-4.0.1/lib/patches/sql_patches.rb +74 -0
- data/rack-mini-profiler-4.0.1/lib/prepend_mysql2_patch.rb +5 -0
- data/rack-mini-profiler-4.0.1/lib/prepend_net_http_patch.rb +5 -0
- data/rack-mini-profiler-4.0.1/lib/prepend_pg_patch.rb +5 -0
- data/rack-mini-profiler-4.0.1/lib/rack-mini-profiler.rb +13 -0
- data/rack-mini-profiler-4.0.1/rack-mini-profiler.gemspec +49 -0
- metadata +125 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Rack
|
|
3
|
+
class MiniProfiler
|
|
4
|
+
module Actions
|
|
5
|
+
def serve_snapshot(env)
|
|
6
|
+
MiniProfiler.authorize_request
|
|
7
|
+
status = 200
|
|
8
|
+
headers = { 'content-type' => 'text/html' }
|
|
9
|
+
qp = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
|
10
|
+
if group_name = qp["group_name"]
|
|
11
|
+
list = @storage.snapshots_group(group_name)
|
|
12
|
+
list.each do |snapshot|
|
|
13
|
+
snapshot[:url] = url_for_snapshot(snapshot[:id], group_name)
|
|
14
|
+
end
|
|
15
|
+
data = {
|
|
16
|
+
group_name: group_name,
|
|
17
|
+
list: list
|
|
18
|
+
}
|
|
19
|
+
else
|
|
20
|
+
list = @storage.snapshots_overview
|
|
21
|
+
list.each do |group|
|
|
22
|
+
group[:url] = url_for_snapshots_group(group[:name])
|
|
23
|
+
end
|
|
24
|
+
data = {
|
|
25
|
+
page: "overview",
|
|
26
|
+
list: list
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
data_html = <<~HTML
|
|
30
|
+
<div style="display: none;" id="snapshots-data">
|
|
31
|
+
#{data.to_json}
|
|
32
|
+
</div>
|
|
33
|
+
HTML
|
|
34
|
+
response = Rack::Response.new([], status, headers)
|
|
35
|
+
|
|
36
|
+
response.write <<~HTML
|
|
37
|
+
<!DOCTYPE html>
|
|
38
|
+
<html>
|
|
39
|
+
<head>
|
|
40
|
+
<title>Rack::MiniProfiler Snapshots</title>
|
|
41
|
+
</head>
|
|
42
|
+
<body class="mp-snapshots">
|
|
43
|
+
HTML
|
|
44
|
+
response.write(data_html)
|
|
45
|
+
script = self.get_profile_script(env)
|
|
46
|
+
response.write(script)
|
|
47
|
+
response.write <<~HTML
|
|
48
|
+
</body>
|
|
49
|
+
</html>
|
|
50
|
+
HTML
|
|
51
|
+
response.finish
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def serve_file(env, file_name:)
|
|
55
|
+
resources_env = env.dup
|
|
56
|
+
resources_env['PATH_INFO'] = file_name
|
|
57
|
+
|
|
58
|
+
if Gem::Version.new(Rack.release) >= Gem::Version.new("2.1.0")
|
|
59
|
+
rack_file = Rack::Files.new(resources_root, 'cache-control' => "max-age=#{cache_control_value}")
|
|
60
|
+
else
|
|
61
|
+
rack_file = Rack::File.new(resources_root, 'cache-control' => "max-age=#{cache_control_value}")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
rack_file.call(resources_env)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def serve_results(env)
|
|
68
|
+
request = Rack::Request.new(env)
|
|
69
|
+
id = request.params['id']
|
|
70
|
+
group_name = request.params['group']
|
|
71
|
+
is_snapshot = group_name && group_name.size > 0
|
|
72
|
+
if is_snapshot
|
|
73
|
+
page_struct = @storage.load_snapshot(id, group_name)
|
|
74
|
+
else
|
|
75
|
+
page_struct = @storage.load(id)
|
|
76
|
+
end
|
|
77
|
+
if !page_struct && is_snapshot
|
|
78
|
+
id = ERB::Util.html_escape(id)
|
|
79
|
+
return [404, {}, ["Snapshot with id '#{id}' not found"]]
|
|
80
|
+
elsif !page_struct
|
|
81
|
+
@storage.set_viewed(user(env), id)
|
|
82
|
+
id = ERB::Util.html_escape(id)
|
|
83
|
+
user_info = ERB::Util.html_escape(user(env))
|
|
84
|
+
return [404, {}, ["Request not found: #{id} - user #{user_info}"]]
|
|
85
|
+
end
|
|
86
|
+
if !page_struct[:has_user_viewed] && !is_snapshot
|
|
87
|
+
page_struct[:client_timings] = TimerStruct::Client.init_from_form_data(env, page_struct)
|
|
88
|
+
page_struct[:has_user_viewed] = true
|
|
89
|
+
@storage.save(page_struct)
|
|
90
|
+
@storage.set_viewed(user(env), id)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# If we're an XMLHttpRequest, serve up the contents as JSON
|
|
94
|
+
if request.xhr?
|
|
95
|
+
result_json = page_struct.to_json
|
|
96
|
+
[200, { 'content-type' => 'application/json' }, [result_json]]
|
|
97
|
+
else
|
|
98
|
+
# Otherwise give the HTML back
|
|
99
|
+
html = generate_html(page_struct, env)
|
|
100
|
+
[200, { 'content-type' => 'text/html' }, [html]]
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def serve_flamegraph(env)
|
|
105
|
+
request = Rack::Request.new(env)
|
|
106
|
+
id = request.params['id']
|
|
107
|
+
page_struct = @storage.load(id)
|
|
108
|
+
|
|
109
|
+
if !page_struct
|
|
110
|
+
id = ERB::Util.html_escape(id)
|
|
111
|
+
user_info = ERB::Util.html_escape(user(env))
|
|
112
|
+
return [404, {}, ["Request not found: #{id} - user #{user_info}"]]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if !page_struct[:flamegraph]
|
|
116
|
+
return [404, {}, ["No flamegraph available for #{ERB::Util.html_escape(id)}"]]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
self.flamegraph(page_struct[:flamegraph], page_struct[:request_path], env)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def serve_profile_gc(env, client_settings)
|
|
123
|
+
return tool_disabled_message(client_settings) if !advanced_debugging_enabled?
|
|
124
|
+
|
|
125
|
+
client_settings.handle_cookie(Rack::MiniProfiler::GCProfiler.new.profile_gc(@app, env))
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def serve_profile_memory(env, client_settings)
|
|
129
|
+
return tool_disabled_message(client_settings) if !advanced_debugging_enabled?
|
|
130
|
+
|
|
131
|
+
unless defined?(MemoryProfiler) && MemoryProfiler.respond_to?(:report)
|
|
132
|
+
message = "Please install the memory_profiler gem and require it: add gem 'memory_profiler' to your Gemfile"
|
|
133
|
+
_status, headers, body = @app.call(env)
|
|
134
|
+
body.close if body.respond_to? :close
|
|
135
|
+
|
|
136
|
+
return client_settings.handle_cookie(
|
|
137
|
+
text_result(message, status: 500, headers: headers)
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
query_params = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
|
142
|
+
options = {
|
|
143
|
+
ignore_files: query_params['memory_profiler_ignore_files'],
|
|
144
|
+
allow_files: query_params['memory_profiler_allow_files'],
|
|
145
|
+
}
|
|
146
|
+
options[:top] = Integer(query_params['memory_profiler_top']) if query_params.key?('memory_profiler_top')
|
|
147
|
+
result = StringIO.new
|
|
148
|
+
report = MemoryProfiler.report(options) do
|
|
149
|
+
_, _, body = @app.call(env)
|
|
150
|
+
body.close if body.respond_to? :close
|
|
151
|
+
end
|
|
152
|
+
report.pretty_print(result)
|
|
153
|
+
client_settings.handle_cookie(text_result(result.string))
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rack
|
|
4
|
+
class MiniProfiler
|
|
5
|
+
class ClientSettings
|
|
6
|
+
|
|
7
|
+
COOKIE_NAME = "__profilin"
|
|
8
|
+
|
|
9
|
+
BACKTRACE_DEFAULT = nil
|
|
10
|
+
BACKTRACE_FULL = 1
|
|
11
|
+
BACKTRACE_NONE = 2
|
|
12
|
+
|
|
13
|
+
attr_accessor :disable_profiling
|
|
14
|
+
attr_accessor :backtrace_level
|
|
15
|
+
|
|
16
|
+
def initialize(env, store, start)
|
|
17
|
+
@request = ::Rack::Request.new(env)
|
|
18
|
+
@cookie = @request.cookies[COOKIE_NAME]
|
|
19
|
+
@store = store
|
|
20
|
+
@start = start
|
|
21
|
+
@backtrace_level = nil
|
|
22
|
+
@orig_disable_profiling = @disable_profiling = nil
|
|
23
|
+
|
|
24
|
+
@allowed_tokens, @orig_auth_tokens = nil
|
|
25
|
+
|
|
26
|
+
if @cookie
|
|
27
|
+
@cookie.split(",").map { |pair| pair.split("=") }.each do |k, v|
|
|
28
|
+
@orig_disable_profiling = @disable_profiling = (v == 't') if k == "dp"
|
|
29
|
+
@backtrace_level = v.to_i if k == "bt"
|
|
30
|
+
@orig_auth_tokens = v.to_s.split("|") if k == "a"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if !@backtrace_level.nil? && (@backtrace_level == 0 || @backtrace_level > BACKTRACE_NONE)
|
|
35
|
+
@backtrace_level = nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
@orig_backtrace_level = @backtrace_level
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def handle_cookie(result)
|
|
43
|
+
status, headers, _body = result
|
|
44
|
+
|
|
45
|
+
if (MiniProfiler.config.authorization_mode == :allow_authorized && !MiniProfiler.request_authorized?)
|
|
46
|
+
# this is non-obvious, don't kill the profiling cookie on errors or short requests
|
|
47
|
+
# this ensures that stuff that never reaches the rails stack does not kill profiling
|
|
48
|
+
if status.to_i >= 200 && status.to_i < 300 && ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - @start) > 0.1)
|
|
49
|
+
discard_cookie!(headers)
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
write!(headers)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
result
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def write!(headers)
|
|
59
|
+
|
|
60
|
+
tokens_changed = false
|
|
61
|
+
|
|
62
|
+
if MiniProfiler.request_authorized? && MiniProfiler.config.authorization_mode == :allow_authorized
|
|
63
|
+
@allowed_tokens ||= @store.allowed_tokens
|
|
64
|
+
tokens_changed = !@orig_auth_tokens || ((@allowed_tokens - @orig_auth_tokens).length > 0)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
if @orig_disable_profiling != @disable_profiling ||
|
|
68
|
+
@orig_backtrace_level != @backtrace_level ||
|
|
69
|
+
@cookie.nil? ||
|
|
70
|
+
tokens_changed
|
|
71
|
+
|
|
72
|
+
settings = { "p" => "t" }
|
|
73
|
+
settings["dp"] = "t" if @disable_profiling
|
|
74
|
+
settings["bt"] = @backtrace_level if @backtrace_level
|
|
75
|
+
settings["a"] = @allowed_tokens.join("|") if @allowed_tokens && MiniProfiler.request_authorized?
|
|
76
|
+
settings_string = settings.map { |k, v| "#{k}=#{v}" }.join(",")
|
|
77
|
+
cookie = { value: settings_string, path: MiniProfiler.config.cookie_path, httponly: true }
|
|
78
|
+
cookie[:secure] = true if @request.ssl?
|
|
79
|
+
cookie[:same_site] = 'Lax'
|
|
80
|
+
Rack::Utils.set_cookie_header!(headers, COOKIE_NAME, cookie)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def discard_cookie!(headers)
|
|
85
|
+
if @cookie
|
|
86
|
+
Rack::Utils.delete_cookie_header!(headers, COOKIE_NAME, path: MiniProfiler.config.cookie_path)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def has_valid_cookie?
|
|
91
|
+
valid_cookie = !@cookie.nil?
|
|
92
|
+
|
|
93
|
+
if (MiniProfiler.config.authorization_mode == :allow_authorized) && valid_cookie
|
|
94
|
+
begin
|
|
95
|
+
@allowed_tokens ||= @store.allowed_tokens
|
|
96
|
+
rescue => e
|
|
97
|
+
if MiniProfiler.config.storage_failure != nil
|
|
98
|
+
MiniProfiler.config.storage_failure.call(e)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
valid_cookie = @allowed_tokens &&
|
|
103
|
+
(Array === @orig_auth_tokens) &&
|
|
104
|
+
((@allowed_tokens & @orig_auth_tokens).length > 0)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
valid_cookie
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def disable_profiling?
|
|
111
|
+
@disable_profiling
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def backtrace_full?
|
|
115
|
+
@backtrace_level == BACKTRACE_FULL
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def backtrace_default?
|
|
119
|
+
@backtrace_level == BACKTRACE_DEFAULT
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def backtrace_none?
|
|
123
|
+
@backtrace_level == BACKTRACE_NONE
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rack
|
|
4
|
+
class MiniProfiler
|
|
5
|
+
class Config
|
|
6
|
+
def self.attr_accessor(*vars)
|
|
7
|
+
@attributes ||= []
|
|
8
|
+
@attributes.concat vars
|
|
9
|
+
super(*vars)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.attributes
|
|
13
|
+
@attributes
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.default
|
|
17
|
+
new.instance_eval {
|
|
18
|
+
@auto_inject = true # automatically inject on every html page
|
|
19
|
+
@base_url_path = "/mini-profiler-resources/".dup
|
|
20
|
+
@cookie_path = "/".dup
|
|
21
|
+
@disable_caching = true
|
|
22
|
+
# called prior to rack chain, to ensure we are allowed to profile
|
|
23
|
+
@pre_authorize_cb = lambda { |env| true }
|
|
24
|
+
|
|
25
|
+
# called after rack chain, to ensure we are REALLY allowed to profile
|
|
26
|
+
@skip_schema_queries = false
|
|
27
|
+
@storage = MiniProfiler::MemoryStore
|
|
28
|
+
@user_provider = Proc.new { |env| Rack::Request.new(env).ip }
|
|
29
|
+
@authorization_mode = :allow_all
|
|
30
|
+
@backtrace_threshold_ms = 0
|
|
31
|
+
@flamegraph_sample_rate = 0.5
|
|
32
|
+
@flamegraph_mode = :wall
|
|
33
|
+
@flamegraph_ignore_gc = false
|
|
34
|
+
@storage_failure = Proc.new do |exception|
|
|
35
|
+
if @logger
|
|
36
|
+
@logger.warn("MiniProfiler storage failure: #{exception.message}")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
@enabled = true
|
|
40
|
+
@max_sql_param_length = 0 # disable sql parameter collection by default
|
|
41
|
+
@skip_sql_param_names = /password/ # skips parameters with the name password by default
|
|
42
|
+
@enable_advanced_debugging_tools = false
|
|
43
|
+
@snapshot_every_n_requests = -1
|
|
44
|
+
@max_snapshot_groups = 50
|
|
45
|
+
@max_snapshots_per_group = 15
|
|
46
|
+
|
|
47
|
+
# ui parameters
|
|
48
|
+
@autorized = true
|
|
49
|
+
@collapse_results = true
|
|
50
|
+
@max_traces_to_show = 20
|
|
51
|
+
@show_children = false
|
|
52
|
+
@show_controls = false
|
|
53
|
+
@show_trivial = false
|
|
54
|
+
@show_total_sql_count = false
|
|
55
|
+
@start_hidden = false
|
|
56
|
+
@toggle_shortcut = 'alt+p'
|
|
57
|
+
@html_container = 'body'
|
|
58
|
+
@position = "top-left"
|
|
59
|
+
@snapshot_hidden_custom_fields = []
|
|
60
|
+
@snapshots_transport_destination_url = nil
|
|
61
|
+
@snapshots_transport_auth_key = nil
|
|
62
|
+
@snapshots_redact_sql_queries = true
|
|
63
|
+
@snapshots_transport_gzip_requests = false
|
|
64
|
+
@enable_hotwire_turbo_drive_support = false
|
|
65
|
+
|
|
66
|
+
@profile_parameter = "pp"
|
|
67
|
+
|
|
68
|
+
self
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
attr_accessor :authorization_mode, :auto_inject, :backtrace_ignores,
|
|
73
|
+
:backtrace_includes, :backtrace_remove, :backtrace_threshold_ms,
|
|
74
|
+
:base_url_path, :cookie_path, :disable_caching, :enabled,
|
|
75
|
+
:flamegraph_sample_rate, :logger, :pre_authorize_cb, :skip_paths,
|
|
76
|
+
:skip_schema_queries, :storage, :storage_failure, :storage_instance,
|
|
77
|
+
:storage_options, :user_provider, :enable_advanced_debugging_tools,
|
|
78
|
+
:skip_sql_param_names, :suppress_encoding, :max_sql_param_length,
|
|
79
|
+
:content_security_policy_nonce, :enable_hotwire_turbo_drive_support,
|
|
80
|
+
:flamegraph_mode, :flamegraph_ignore_gc, :profile_parameter
|
|
81
|
+
|
|
82
|
+
# ui accessors
|
|
83
|
+
attr_accessor :collapse_results, :max_traces_to_show, :position,
|
|
84
|
+
:show_children, :show_controls, :show_trivial, :show_total_sql_count,
|
|
85
|
+
:start_hidden, :toggle_shortcut, :html_container
|
|
86
|
+
|
|
87
|
+
# snapshot related config
|
|
88
|
+
attr_accessor :snapshot_every_n_requests, :max_snapshots_per_group,
|
|
89
|
+
:snapshot_hidden_custom_fields, :snapshots_transport_destination_url,
|
|
90
|
+
:snapshots_transport_auth_key, :snapshots_redact_sql_queries,
|
|
91
|
+
:snapshots_transport_gzip_requests, :max_snapshot_groups
|
|
92
|
+
|
|
93
|
+
# Deprecated options
|
|
94
|
+
attr_accessor :use_existing_jquery
|
|
95
|
+
|
|
96
|
+
attr_reader :assets_url
|
|
97
|
+
|
|
98
|
+
# redefined - since the accessor defines it first
|
|
99
|
+
undef :authorization_mode=
|
|
100
|
+
# rubocop:disable Lint/DuplicateMethods
|
|
101
|
+
def authorization_mode=(mode)
|
|
102
|
+
# rubocop:enable Lint/DuplicateMethods
|
|
103
|
+
if mode == :whitelist
|
|
104
|
+
warn "[DEPRECATION] `:whitelist` authorization mode is deprecated. Please use `:allow_authorized` instead."
|
|
105
|
+
|
|
106
|
+
mode = :allow_authorized
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
warn <<~DEP unless mode == :allow_authorized || mode == :allow_all
|
|
110
|
+
[DEPRECATION] unknown authorization mode #{mode}. Expected `:allow_all` or `:allow_authorized`.
|
|
111
|
+
DEP
|
|
112
|
+
|
|
113
|
+
@authorization_mode = mode
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def assets_url=(lmbda)
|
|
117
|
+
if defined?(Rack::MiniProfilerRails)
|
|
118
|
+
Rack::MiniProfilerRails.create_engine
|
|
119
|
+
end
|
|
120
|
+
@assets_url = lmbda
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def vertical_position
|
|
124
|
+
position.include?('bottom') ? 'bottom' : 'top'
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def horizontal_position
|
|
128
|
+
position.include?('right') ? 'right' : 'left'
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def merge!(config)
|
|
132
|
+
if config
|
|
133
|
+
if Hash === config
|
|
134
|
+
config.each { |k, v| instance_variable_set "@#{k}", v }
|
|
135
|
+
else
|
|
136
|
+
self.class.attributes.each { |k|
|
|
137
|
+
v = config.send k
|
|
138
|
+
instance_variable_set "@#{k}", v if v
|
|
139
|
+
}
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Rack::MiniProfiler::Context
|
|
4
|
+
attr_accessor :inject_js, :current_timer, :page_struct, :skip_backtrace,
|
|
5
|
+
:full_backtrace, :discard, :mpt_init, :measure
|
|
6
|
+
|
|
7
|
+
def initialize(opts = {})
|
|
8
|
+
opts["measure"] = true unless opts.key? "measure"
|
|
9
|
+
opts.each do |k, v|
|
|
10
|
+
self.instance_variable_set('@' + k, v)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Rack::MiniProfiler::GCProfiler
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
@ignore = []
|
|
7
|
+
@ignore << @ignore.__id__
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def object_space_stats
|
|
11
|
+
stats = Hash.new(0).compare_by_identity
|
|
12
|
+
ids = Hash.new.compare_by_identity
|
|
13
|
+
|
|
14
|
+
@ignore << stats.__id__
|
|
15
|
+
@ignore << ids.__id__
|
|
16
|
+
|
|
17
|
+
ObjectSpace.each_object { |o|
|
|
18
|
+
begin
|
|
19
|
+
stats[o.class] += 1
|
|
20
|
+
ids[o.__id__] = o if Integer === o.__id__
|
|
21
|
+
rescue NoMethodError
|
|
22
|
+
# protect against BasicObject
|
|
23
|
+
end
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@ignore.each do |id|
|
|
27
|
+
if ids.delete(id)
|
|
28
|
+
klass = ObjectSpace._id2ref(id).class
|
|
29
|
+
stats[klass] -= 1
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
result = { stats: stats, ids: ids }
|
|
34
|
+
@ignore << result.__id__
|
|
35
|
+
|
|
36
|
+
result
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def diff_object_stats(before, after)
|
|
40
|
+
diff = {}.compare_by_identity
|
|
41
|
+
after.each do |k, v|
|
|
42
|
+
diff[k] = v - before[k]
|
|
43
|
+
end
|
|
44
|
+
before.each do |k, v|
|
|
45
|
+
diff[k] = 0 - v unless after.has_key?(k)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
diff
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def analyze_strings(ids_before, ids_after)
|
|
52
|
+
result = {}
|
|
53
|
+
ids_after.each do |id, _|
|
|
54
|
+
obj = ObjectSpace._id2ref(id)
|
|
55
|
+
if String === obj && !ids_before.include?(obj.object_id)
|
|
56
|
+
result[obj] ||= 0
|
|
57
|
+
result[obj] += 1
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
result
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def analyze_growth(ids_before, ids_after)
|
|
64
|
+
new_objects = 0
|
|
65
|
+
memory_allocated = 0
|
|
66
|
+
|
|
67
|
+
ids_after.each do |id, _|
|
|
68
|
+
if !ids_before.include?(id) && obj = ObjectSpace._id2ref(id)
|
|
69
|
+
# this is going to be version specific (may change in 2.1)
|
|
70
|
+
size = ObjectSpace.memsize_of(obj)
|
|
71
|
+
memory_allocated += size
|
|
72
|
+
new_objects += 1
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
[new_objects, memory_allocated]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def analyze_initial_state(ids_before)
|
|
80
|
+
memory_allocated = 0
|
|
81
|
+
objects = 0
|
|
82
|
+
|
|
83
|
+
ids_before.each do |id, _|
|
|
84
|
+
if obj = ObjectSpace._id2ref(id)
|
|
85
|
+
# this is going to be version specific (may change in 2.1)
|
|
86
|
+
memory_allocated += ObjectSpace.memsize_of(obj)
|
|
87
|
+
objects += 1
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
[objects, memory_allocated]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def profile_gc(app, env)
|
|
95
|
+
|
|
96
|
+
# for memsize_of
|
|
97
|
+
require 'objspace'
|
|
98
|
+
|
|
99
|
+
# clean up before
|
|
100
|
+
GC.start
|
|
101
|
+
stat = GC.stat
|
|
102
|
+
prev_gc_state = GC.disable
|
|
103
|
+
stat_before = object_space_stats
|
|
104
|
+
b = app.call(env)[2]
|
|
105
|
+
b.close if b.respond_to? :close
|
|
106
|
+
stat_after = object_space_stats
|
|
107
|
+
# so we don't blow out on memory
|
|
108
|
+
prev_gc_state ? GC.disable : GC.enable
|
|
109
|
+
|
|
110
|
+
diff = diff_object_stats(stat_before[:stats], stat_after[:stats])
|
|
111
|
+
string_analysis = analyze_strings(stat_before[:ids], stat_after[:ids])
|
|
112
|
+
new_objects, memory_allocated = analyze_growth(stat_before[:ids], stat_after[:ids])
|
|
113
|
+
objects_before, memory_before = analyze_initial_state(stat_before[:ids])
|
|
114
|
+
|
|
115
|
+
body = []
|
|
116
|
+
|
|
117
|
+
body << "
|
|
118
|
+
Overview
|
|
119
|
+
--------
|
|
120
|
+
Initial state: object count: #{objects_before}
|
|
121
|
+
Memory allocated outside heap (bytes): #{memory_before}
|
|
122
|
+
|
|
123
|
+
GC Stats:
|
|
124
|
+
--------
|
|
125
|
+
#{stat.map { |k, v| "#{k} : #{v}" }.sort!.join("\n")}
|
|
126
|
+
|
|
127
|
+
New bytes allocated outside of Ruby heaps: #{memory_allocated}
|
|
128
|
+
New objects: #{new_objects}
|
|
129
|
+
"
|
|
130
|
+
|
|
131
|
+
body << "
|
|
132
|
+
ObjectSpace delta caused by request:
|
|
133
|
+
-----------------------------------\n"
|
|
134
|
+
diff.to_a.delete_if { |_k, v| v == 0 }.sort_by! { |_k, v| v }.reverse_each do |k, v|
|
|
135
|
+
body << "#{k} : #{v}\n"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
body << "\n
|
|
139
|
+
ObjectSpace stats:
|
|
140
|
+
-----------------\n"
|
|
141
|
+
|
|
142
|
+
stat_after[:stats].to_a.sort_by! { |_k, v| v }.reverse_each do |k, v|
|
|
143
|
+
body << "#{k} : #{v}\n"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
body << "\n
|
|
147
|
+
String stats:
|
|
148
|
+
------------\n"
|
|
149
|
+
|
|
150
|
+
string_analysis.to_a.sort_by! { |_k, v| -v }.take(1000).each do |string, count|
|
|
151
|
+
body << "#{count} : #{string}\n"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
[200, { Rack::CONTENT_TYPE => 'text/plain' }, body]
|
|
155
|
+
ensure
|
|
156
|
+
prev_gc_state ? GC.disable : GC.enable
|
|
157
|
+
end
|
|
158
|
+
end
|