rack-mini-profiler 0.1.30 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack-mini-profiler might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +149 -0
- data/README.md +271 -0
- data/{Ruby/lib → lib}/html/includes.css +0 -0
- data/{Ruby/lib → lib}/html/includes.js +9 -8
- data/{Ruby/lib → lib}/html/includes.less +0 -0
- data/{Ruby/lib → lib}/html/includes.tmpl +1 -1
- data/{Ruby/lib → lib}/html/jquery.1.7.1.js +0 -0
- data/{Ruby/lib → lib}/html/jquery.tmpl.js +0 -0
- data/{Ruby/lib → lib}/html/list.css +2 -2
- data/{Ruby/lib → lib}/html/list.js +1 -1
- data/{Ruby/lib → lib}/html/list.tmpl +2 -2
- data/lib/html/profile_handler.js +1 -0
- data/{Ruby/lib → lib}/html/share.html +2 -2
- data/{Ruby/lib → lib}/mini_profiler/client_settings.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/client_timer_struct.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/config.rb +11 -4
- data/{Ruby/lib → lib}/mini_profiler/context.rb +1 -1
- data/{Ruby/lib → lib}/mini_profiler/custom_timer_struct.rb +0 -0
- data/lib/mini_profiler/gc_profiler.rb +181 -0
- data/{Ruby/lib → lib}/mini_profiler/page_timer_struct.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/profiler.rb +108 -123
- data/{Ruby/lib → lib}/mini_profiler/profiling_methods.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/request_timer_struct.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/sql_timer_struct.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/storage/abstract_store.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/storage/file_store.rb +26 -4
- data/{Ruby/lib → lib}/mini_profiler/storage/memcache_store.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/storage/memory_store.rb +25 -4
- data/{Ruby/lib → lib}/mini_profiler/storage/redis_store.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/timer_struct.rb +0 -0
- data/lib/mini_profiler/version.rb +5 -0
- data/{Ruby/lib → lib}/mini_profiler_rails/railtie.rb +23 -6
- data/{Ruby/lib → lib}/patches/net_patches.rb +0 -0
- data/{Ruby/lib → lib}/patches/sql_patches.rb +15 -8
- data/{Ruby/lib → lib}/rack-mini-profiler.rb +0 -0
- data/rack-mini-profiler.gemspec +24 -16
- metadata +163 -53
- data/Ruby/CHANGELOG +0 -156
- data/Ruby/README.md +0 -172
- data/Ruby/lib/html/flamegraph.html +0 -351
- data/Ruby/lib/html/profile_handler.js +0 -1
- data/Ruby/lib/mini_profiler/flame_graph.rb +0 -54
- data/Ruby/lib/mini_profiler/gc_profiler.rb +0 -107
- data/Ruby/lib/mini_profiler/version.rb +0 -5
File without changes
|
File without changes
|
File without changes
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
tbody tr:nth-child(odd) { background-color:#eee; }
|
2
2
|
tbody tr:nth-child(even) { background-color:#fff; }
|
3
3
|
table { border: 0; border-spacing:0;}
|
4
4
|
tr {border: 0;}
|
@@ -6,4 +6,4 @@ tr {border: 0;}
|
|
6
6
|
td {padding: 8px;}
|
7
7
|
.time {text-align:center;}
|
8
8
|
thead tr {background-color: #bbb; color: #444; font-size: 12px;}
|
9
|
-
thead tr th { padding: 5px 15px;}
|
9
|
+
thead tr th { padding: 5px 15px;}
|
@@ -0,0 +1 @@
|
|
1
|
+
<script async type="text/javascript" id="mini-profiler" src="{path}includes.js?v={version}" data-version="{version}" data-path="{path}" data-current-id="{currentId}" data-ids="{ids}" data-position="{position}" data-trivial="{showTrivial}" data-children="{showChildren}" data-max-traces="{maxTracesToShow}" data-controls="{showControls}" data-authorized="{authorized}" data-toggle-shortcut="{toggleShortcut}" data-start-hidden="{startHidden}"></script>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
<html>
|
2
2
|
<head>
|
3
3
|
<title>{name} ({duration} ms) - Profiling Results</title>
|
4
4
|
<script type='text/javascript' src='{path}jquery.1.7.1.js?v={version}'></script>
|
@@ -8,4 +8,4 @@
|
|
8
8
|
<body>
|
9
9
|
<div class='profiler-result-full'></div>
|
10
10
|
</body>
|
11
|
-
</html>
|
11
|
+
</html>
|
File without changes
|
File without changes
|
@@ -12,10 +12,10 @@ module Rack
|
|
12
12
|
@attributes
|
13
13
|
end
|
14
14
|
|
15
|
-
attr_accessor :auto_inject, :
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
attr_accessor :authorization_mode, :auto_inject, :backtrace_ignores, :backtrace_includes, :backtrace_remove,
|
16
|
+
:backtrace_threshold_ms, :base_url_path, :enabled, :flamegraph_sample_rate, :logger, :position,
|
17
|
+
:pre_authorize_cb, :skip_paths, :skip_schema_queries, :start_hidden, :storage, :storage_failure,
|
18
|
+
:storage_instance, :storage_options, :toggle_shortcut, :user_provider
|
19
19
|
|
20
20
|
# Deprecated options
|
21
21
|
attr_accessor :use_existing_jquery
|
@@ -37,6 +37,13 @@ module Rack
|
|
37
37
|
@toggle_shortcut = 'Alt+P'
|
38
38
|
@start_hidden = false
|
39
39
|
@backtrace_threshold_ms = 0
|
40
|
+
@flamegraph_sample_rate = 0.5
|
41
|
+
@storage_failure = Proc.new do |exception|
|
42
|
+
if @logger
|
43
|
+
@logger.warn("MiniProfiler storage failure: #{exception.message}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@enabled = true
|
40
47
|
self
|
41
48
|
}
|
42
49
|
end
|
File without changes
|
@@ -0,0 +1,181 @@
|
|
1
|
+
class Rack::MiniProfiler::GCProfiler
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
@ignore = []
|
5
|
+
@ignore << @ignore.__id__
|
6
|
+
end
|
7
|
+
|
8
|
+
def object_space_stats
|
9
|
+
stats = {}
|
10
|
+
ids = {}
|
11
|
+
|
12
|
+
@ignore << stats.__id__
|
13
|
+
@ignore << ids.__id__
|
14
|
+
|
15
|
+
i=0
|
16
|
+
ObjectSpace.each_object { |o|
|
17
|
+
begin
|
18
|
+
i = stats[o.class] || 0
|
19
|
+
i += 1
|
20
|
+
stats[o.class] = i
|
21
|
+
ids[o.__id__] = o if Integer === o.__id__
|
22
|
+
rescue NoMethodError
|
23
|
+
# protect against BasicObject
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
@ignore.each do |id|
|
28
|
+
if ids.delete(id)
|
29
|
+
klass = ObjectSpace._id2ref(id).class
|
30
|
+
stats[klass] -= 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
result = {:stats => stats, :ids => ids}
|
35
|
+
@ignore << result.__id__
|
36
|
+
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
def diff_object_stats(before,after)
|
41
|
+
diff = {}
|
42
|
+
after.each do |k,v|
|
43
|
+
diff[k] = v - (before[k] || 0)
|
44
|
+
end
|
45
|
+
before.each do |k,v|
|
46
|
+
diff[k] = 0 - v unless after[k]
|
47
|
+
end
|
48
|
+
|
49
|
+
diff
|
50
|
+
end
|
51
|
+
|
52
|
+
def analyze_strings(ids_before,ids_after)
|
53
|
+
result = {}
|
54
|
+
ids_after.each do |id,_|
|
55
|
+
obj = ObjectSpace._id2ref(id)
|
56
|
+
if String === obj && !ids_before.include?(obj.object_id)
|
57
|
+
result[obj] ||= 0
|
58
|
+
result[obj] += 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
64
|
+
def analyze_growth(ids_before, ids_after)
|
65
|
+
new_objects = 0
|
66
|
+
memory_allocated = 0
|
67
|
+
|
68
|
+
ids_after.each do |id,_|
|
69
|
+
if !ids_before.include?(id) && obj=ObjectSpace._id2ref(id)
|
70
|
+
# this is going to be version specific (may change in 2.1)
|
71
|
+
size = ObjectSpace.memsize_of(obj)
|
72
|
+
memory_allocated += size
|
73
|
+
new_objects += 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
[new_objects, memory_allocated]
|
78
|
+
end
|
79
|
+
|
80
|
+
def analyze_initial_state(ids_before)
|
81
|
+
memory_allocated = 0
|
82
|
+
objects = 0
|
83
|
+
|
84
|
+
ids_before.each do |id,_|
|
85
|
+
if obj=ObjectSpace._id2ref(id)
|
86
|
+
# this is going to be version specific (may change in 2.1)
|
87
|
+
memory_allocated += ObjectSpace.memsize_of(obj)
|
88
|
+
objects += 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
[objects,memory_allocated]
|
93
|
+
end
|
94
|
+
|
95
|
+
def profile_gc_time(app,env)
|
96
|
+
body = []
|
97
|
+
|
98
|
+
begin
|
99
|
+
GC::Profiler.clear
|
100
|
+
prev_profiler_state = GC::Profiler.enabled?
|
101
|
+
prev_gc_state = GC.enable
|
102
|
+
GC::Profiler.enable
|
103
|
+
b = app.call(env)[2]
|
104
|
+
b.close if b.respond_to? :close
|
105
|
+
body << "GC Profiler ran during this request, if it fired you will see the cost below:\n\n"
|
106
|
+
body << GC::Profiler.result
|
107
|
+
ensure
|
108
|
+
prev_gc_state ? GC.disable : GC.enable
|
109
|
+
GC::Profiler.disable unless prev_profiler_state
|
110
|
+
end
|
111
|
+
|
112
|
+
return [200, {'Content-Type' => 'text/plain'}, body]
|
113
|
+
end
|
114
|
+
|
115
|
+
def profile_gc(app,env)
|
116
|
+
|
117
|
+
# for memsize_of
|
118
|
+
require 'objspace'
|
119
|
+
|
120
|
+
body = [];
|
121
|
+
|
122
|
+
stat_before,stat_after,diff,string_analysis,
|
123
|
+
new_objects, memory_allocated, stat, memory_before, objects_before = nil
|
124
|
+
|
125
|
+
# clean up before
|
126
|
+
GC.start
|
127
|
+
stat = GC.stat
|
128
|
+
prev_gc_state = GC.disable
|
129
|
+
stat_before = object_space_stats
|
130
|
+
b = app.call(env)[2]
|
131
|
+
b.close if b.respond_to? :close
|
132
|
+
stat_after = object_space_stats
|
133
|
+
# so we don't blow out on memory
|
134
|
+
prev_gc_state ? GC.disable : GC.enable
|
135
|
+
|
136
|
+
diff = diff_object_stats(stat_before[:stats],stat_after[:stats])
|
137
|
+
string_analysis = analyze_strings(stat_before[:ids], stat_after[:ids])
|
138
|
+
new_objects, memory_allocated = analyze_growth(stat_before[:ids], stat_after[:ids])
|
139
|
+
objects_before, memory_before = analyze_initial_state(stat_before[:ids])
|
140
|
+
|
141
|
+
|
142
|
+
body << "
|
143
|
+
Overview
|
144
|
+
------------------------------------
|
145
|
+
Initial state: object count - #{objects_before} , memory allocated outside heap (bytes) #{memory_before}
|
146
|
+
|
147
|
+
GC Stats: #{stat.map{|k,v| "#{k} : #{v}" }.join(", ")}
|
148
|
+
|
149
|
+
New bytes allocated outside of Ruby heaps: #{memory_allocated}
|
150
|
+
New objects: #{new_objects}
|
151
|
+
"
|
152
|
+
|
153
|
+
body << "
|
154
|
+
ObjectSpace delta caused by request:
|
155
|
+
--------------------------------------------\n"
|
156
|
+
diff.to_a.reject{|k,v| v == 0}.sort{|x,y| y[1] <=> x[1]}.each do |k,v|
|
157
|
+
body << "#{k} : #{v}\n" if v != 0
|
158
|
+
end
|
159
|
+
|
160
|
+
body << "\n
|
161
|
+
ObjectSpace stats:
|
162
|
+
-----------------\n"
|
163
|
+
|
164
|
+
stat_after[:stats].to_a.sort{|x,y| y[1] <=> x[1]}.each do |k,v|
|
165
|
+
body << "#{k} : #{v}\n"
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
body << "\n
|
170
|
+
String stats:
|
171
|
+
------------\n"
|
172
|
+
|
173
|
+
string_analysis.to_a.sort{|x,y| y[1] <=> x[1] }.take(1000).each do |string,count|
|
174
|
+
body << "#{count} : #{string}\n"
|
175
|
+
end
|
176
|
+
|
177
|
+
return [200, {'Content-Type' => 'text/plain'}, body]
|
178
|
+
ensure
|
179
|
+
prev_gc_state ? GC.disable : GC.enable
|
180
|
+
end
|
181
|
+
end
|
File without changes
|
@@ -18,7 +18,8 @@ require 'mini_profiler/profiling_methods'
|
|
18
18
|
require 'mini_profiler/context'
|
19
19
|
require 'mini_profiler/client_settings'
|
20
20
|
require 'mini_profiler/gc_profiler'
|
21
|
-
|
21
|
+
# TODO
|
22
|
+
# require 'mini_profiler/gc_profiler_ruby_head' if Gem::Version.new('2.1.0') <= Gem::Version.new(RUBY_VERSION)
|
22
23
|
|
23
24
|
module Rack
|
24
25
|
|
@@ -106,7 +107,9 @@ module Rack
|
|
106
107
|
page_struct = @storage.load(id)
|
107
108
|
unless page_struct
|
108
109
|
@storage.set_viewed(user(env), id)
|
109
|
-
|
110
|
+
id = ERB::Util.html_escape(request['id'])
|
111
|
+
user_info = ERB::Util.html_escape(user(env))
|
112
|
+
return [404, {}, ["Request not found: #{id} - user #{user_info}"]]
|
110
113
|
end
|
111
114
|
unless page_struct['HasUserViewed']
|
112
115
|
page_struct['ClientTimings'] = ClientTimerStruct.init_from_form_data(env, page_struct)
|
@@ -137,7 +140,9 @@ module Rack
|
|
137
140
|
|
138
141
|
def serve_html(env)
|
139
142
|
file_name = env['PATH_INFO'][(@config.base_url_path.length)..1000]
|
143
|
+
|
140
144
|
return serve_results(env) if file_name.eql?('results')
|
145
|
+
|
141
146
|
full_path = ::File.expand_path("../html/#{file_name}", ::File.dirname(__FILE__))
|
142
147
|
return [404, {}, ["Not found"]] unless ::File.exists? full_path
|
143
148
|
f = Rack::File.new nil
|
@@ -201,11 +206,12 @@ module Rack
|
|
201
206
|
skip_it = true
|
202
207
|
end
|
203
208
|
|
204
|
-
if query_string =~ /pp=enable/
|
209
|
+
if query_string =~ /pp=enable/ && (@config.authorization_mode != :whitelist || MiniProfiler.request_authorized?)
|
205
210
|
skip_it = false
|
211
|
+
config.enabled = true
|
206
212
|
end
|
207
213
|
|
208
|
-
if skip_it
|
214
|
+
if skip_it || !config.enabled
|
209
215
|
status,headers,body = @app.call(env)
|
210
216
|
client_settings.disable_profiling = true
|
211
217
|
client_settings.write!(headers)
|
@@ -215,8 +221,18 @@ module Rack
|
|
215
221
|
end
|
216
222
|
|
217
223
|
if query_string =~ /pp=profile-gc/
|
224
|
+
current.measure = false if current
|
225
|
+
|
218
226
|
if query_string =~ /pp=profile-gc-time/
|
219
227
|
return Rack::MiniProfiler::GCProfiler.new.profile_gc_time(@app, env)
|
228
|
+
elsif query_string =~ /pp=profile-gc-ruby-head/
|
229
|
+
result = StringIO.new
|
230
|
+
report = MemoryProfiler.report do
|
231
|
+
_,_,body = @app.call(env)
|
232
|
+
body.close if body.respond_to? :close
|
233
|
+
end
|
234
|
+
report.pretty_print(result)
|
235
|
+
return text_result(result.string)
|
220
236
|
else
|
221
237
|
return Rack::MiniProfiler::GCProfiler.new.profile_gc(@app, env)
|
222
238
|
end
|
@@ -237,35 +253,7 @@ module Rack
|
|
237
253
|
current.skip_backtrace = true
|
238
254
|
end
|
239
255
|
|
240
|
-
|
241
|
-
quit_sampler = false
|
242
|
-
backtraces = nil
|
243
|
-
|
244
|
-
if query_string =~ /pp=sample/ || query_string =~ /pp=flamegraph/
|
245
|
-
current.measure = false
|
246
|
-
skip_frames = 0
|
247
|
-
backtraces = []
|
248
|
-
t = Thread.current
|
249
|
-
|
250
|
-
Thread.new {
|
251
|
-
# new in Ruby 2.0
|
252
|
-
has_backtrace_locations = t.respond_to?(:backtrace_locations)
|
253
|
-
begin
|
254
|
-
i = 10000 # for sanity never grab more than 10k samples
|
255
|
-
while i > 0
|
256
|
-
break if done_sampling
|
257
|
-
i -= 1
|
258
|
-
backtraces << (has_backtrace_locations ? t.backtrace_locations : t.backtrace)
|
259
|
-
|
260
|
-
# On my machine using Ruby 2.0 this give me excellent fidelity of stack trace per 1.2ms
|
261
|
-
# with this fidelity analysis becomes very powerful
|
262
|
-
sleep 0.0005
|
263
|
-
end
|
264
|
-
ensure
|
265
|
-
quit_sampler = true
|
266
|
-
end
|
267
|
-
}
|
268
|
-
end
|
256
|
+
flamegraph = nil
|
269
257
|
|
270
258
|
trace_exceptions = query_string =~ /pp=trace-exceptions/ && defined? TracePoint
|
271
259
|
status, headers, body, exceptions,trace = nil
|
@@ -287,15 +275,33 @@ module Rack
|
|
287
275
|
env['HTTP_IF_MODIFIED_SINCE'] = ''
|
288
276
|
env['HTTP_IF_NONE_MATCH'] = ''
|
289
277
|
|
290
|
-
|
278
|
+
if query_string =~ /pp=flamegraph/
|
279
|
+
unless defined?(Flamegraph) && Flamegraph.respond_to?(:generate)
|
280
|
+
|
281
|
+
flamegraph = "Please install the flamegraph gem and require it: add gem 'flamegraph' to your Gemfile"
|
282
|
+
status,headers,body = @app.call(env)
|
283
|
+
else
|
284
|
+
# do not sully our profile with mini profiler timings
|
285
|
+
current.measure = false
|
286
|
+
match_data = query_string.match(/flamegraph_sample_rate=([\d\.]+)/)
|
287
|
+
|
288
|
+
mode = query_string =~ /mode=c/ ? :c : :ruby
|
289
|
+
|
290
|
+
if match_data && !match_data[1].to_f.zero?
|
291
|
+
sample_rate = match_data[1].to_f
|
292
|
+
else
|
293
|
+
sample_rate = config.flamegraph_sample_rate
|
294
|
+
end
|
295
|
+
flamegraph = Flamegraph.generate(nil, :fidelity => sample_rate, :embed_resources => query_string =~ /embed/, :mode => mode) do
|
296
|
+
status,headers,body = @app.call(env)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
else
|
300
|
+
status,headers,body = @app.call(env)
|
301
|
+
end
|
291
302
|
client_settings.write!(headers)
|
292
303
|
ensure
|
293
304
|
trace.disable if trace
|
294
|
-
|
295
|
-
if backtraces
|
296
|
-
done_sampling = true
|
297
|
-
sleep 0.001 until quit_sampler
|
298
|
-
end
|
299
305
|
end
|
300
306
|
|
301
307
|
skip_it = current.discard
|
@@ -331,26 +337,27 @@ module Rack
|
|
331
337
|
page_struct['User'] = user(env)
|
332
338
|
page_struct['Root'].record_time((Time.now - start) * 1000)
|
333
339
|
|
334
|
-
if
|
340
|
+
if flamegraph
|
335
341
|
body.close if body.respond_to? :close
|
336
|
-
|
337
|
-
return analyze(backtraces, page_struct)
|
338
|
-
else
|
339
|
-
return flame_graph(backtraces, page_struct)
|
340
|
-
end
|
342
|
+
return self.flamegraph(flamegraph)
|
341
343
|
end
|
342
344
|
|
343
345
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
# inject headers, script
|
349
|
-
if headers['Content-Type'] && status == 200
|
350
|
-
client_settings.write!(headers)
|
346
|
+
begin
|
347
|
+
# no matter what it is, it should be unviewed, otherwise we will miss POST
|
348
|
+
@storage.set_unviewed(page_struct['User'], page_struct['Id'])
|
349
|
+
@storage.save(page_struct)
|
351
350
|
|
352
|
-
|
353
|
-
|
351
|
+
# inject headers, script
|
352
|
+
if headers['Content-Type'] && status == 200
|
353
|
+
client_settings.write!(headers)
|
354
|
+
result = inject_profiler(env,status,headers,body)
|
355
|
+
return result if result
|
356
|
+
end
|
357
|
+
rescue Exception => e
|
358
|
+
if @config.storage_failure != nil
|
359
|
+
@config.storage_failure.call(e)
|
360
|
+
end
|
354
361
|
end
|
355
362
|
|
356
363
|
client_settings.write!(headers)
|
@@ -358,7 +365,7 @@ module Rack
|
|
358
365
|
|
359
366
|
ensure
|
360
367
|
# Make sure this always happens
|
361
|
-
current = nil
|
368
|
+
self.current = nil
|
362
369
|
end
|
363
370
|
|
364
371
|
def inject_profiler(env,status,headers,body)
|
@@ -368,7 +375,7 @@ module Rack
|
|
368
375
|
|
369
376
|
headers.delete('ETag')
|
370
377
|
headers.delete('Date')
|
371
|
-
headers['Cache-Control'] = 'must-revalidate, private, max-age=0'
|
378
|
+
headers['Cache-Control'] = 'no-store, must-revalidate, private, max-age=0'
|
372
379
|
|
373
380
|
# inject header
|
374
381
|
if headers.is_a? Hash
|
@@ -403,9 +410,9 @@ module Rack
|
|
403
410
|
regex = /<\/html>/i
|
404
411
|
close_tag = '</html>'
|
405
412
|
else
|
406
|
-
# implicit </body> and </html>.
|
413
|
+
# implicit </body> and </html>. Don't do anything.
|
407
414
|
|
408
|
-
return fragment
|
415
|
+
return fragment
|
409
416
|
end
|
410
417
|
|
411
418
|
matches = fragment.scan(regex).length
|
@@ -439,7 +446,6 @@ module Rack
|
|
439
446
|
end
|
440
447
|
|
441
448
|
def dump_env(env)
|
442
|
-
headers = {'Content-Type' => 'text/plain'}
|
443
449
|
body = "Rack Environment\n---------------\n"
|
444
450
|
env.each do |k,v|
|
445
451
|
body << "#{k}: #{v}\n"
|
@@ -458,6 +464,11 @@ module Rack
|
|
458
464
|
body << "User #{user(env)}\n"
|
459
465
|
body << config.storage_instance.diagnostics(user(env)) rescue "no diagnostics implemented for storage"
|
460
466
|
|
467
|
+
text_result(body)
|
468
|
+
end
|
469
|
+
|
470
|
+
def text_result(body)
|
471
|
+
headers = {'Content-Type' => 'text/plain'}
|
461
472
|
[200, headers, [body]]
|
462
473
|
end
|
463
474
|
|
@@ -471,12 +482,14 @@ module Rack
|
|
471
482
|
pp=no-backtrace #{"(*) " if client_settings.backtrace_none?}: don't collect stack traces from all the SQL executed (sticky, use pp=normal-backtrace to enable)
|
472
483
|
pp=normal-backtrace #{"(*) " if client_settings.backtrace_default?}: collect stack traces from all the SQL executed and filter normally
|
473
484
|
pp=full-backtrace #{"(*) " if client_settings.backtrace_full?}: enable full backtraces for SQL executed (use pp=normal-backtrace to disable)
|
474
|
-
pp=sample : sample stack traces and return a report isolating heavy usage (works best on Ruby 2.0)
|
475
485
|
pp=disable : disable profiling for this session
|
476
486
|
pp=enable : enable profiling for this session (if previously disabled)
|
477
487
|
pp=profile-gc: perform gc profiling on this request, analyzes ObjectSpace generated by request (ruby 1.9.3 only)
|
478
488
|
pp=profile-gc-time: perform built-in gc profiling on this request (ruby 1.9.3 only)
|
479
|
-
pp=
|
489
|
+
pp=profile-gc-ruby-head: requires the memory_profiler gem, new location based report
|
490
|
+
pp=flamegraph: works best on Ruby 2.0, a graph representing sampled activity (requires the flamegraph gem).
|
491
|
+
pp=flamegraph&flamegraph_sample_rate=1: creates a flamegraph with the specified sample rate (in ms). Overrides value set in config
|
492
|
+
pp=flamegraph_embed: works best on Ruby 2.0, a graph representing sampled activity (requires the flamegraph gem), embedded resources for use on an intranet.
|
480
493
|
pp=trace-exceptions: requires Ruby 2.0, will return all the spots where your application raises execptions
|
481
494
|
"
|
482
495
|
|
@@ -484,61 +497,22 @@ module Rack
|
|
484
497
|
[200, headers, [body]]
|
485
498
|
end
|
486
499
|
|
487
|
-
def
|
488
|
-
graph = FlameGraph.new(traces)
|
489
|
-
data = graph.graph_data
|
490
|
-
|
500
|
+
def flamegraph(graph)
|
491
501
|
headers = {'Content-Type' => 'text/html'}
|
492
|
-
|
493
|
-
body = IO.read(::File.expand_path('../html/flamegraph.html', ::File.dirname(__FILE__)))
|
494
|
-
body.gsub!("/*DATA*/", ::JSON.generate(data));
|
495
|
-
|
496
|
-
[200, headers, [body]]
|
502
|
+
[200, headers, [graph]]
|
497
503
|
end
|
498
504
|
|
499
|
-
def
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
seen = {}
|
504
|
-
fulldump = ""
|
505
|
-
traces.each do |trace|
|
506
|
-
fulldump << "\n\n"
|
507
|
-
distinct = {}
|
508
|
-
trace.each do |frame|
|
509
|
-
frame = frame.to_s unless String === frame
|
510
|
-
unless distinct[frame]
|
511
|
-
distinct[frame] = true
|
512
|
-
seen[frame] ||= 0
|
513
|
-
seen[frame] += 1
|
514
|
-
end
|
515
|
-
fulldump << frame << "\n"
|
516
|
-
end
|
517
|
-
end
|
518
|
-
|
519
|
-
body << "\n\nStack Trace Analysis\n"
|
520
|
-
seen.to_a.sort{|x,y| y[1] <=> x[1]}.each do |name, count|
|
521
|
-
if count > traces.count / 10
|
522
|
-
body << "#{name} x #{count}\n"
|
523
|
-
end
|
524
|
-
end
|
525
|
-
|
526
|
-
body << "\n\n\nRaw traces \n"
|
527
|
-
body << fulldump
|
528
|
-
|
529
|
-
[200, headers, [body]]
|
505
|
+
def ids(env)
|
506
|
+
# cap at 10 ids, otherwise there is a chance you can blow the header
|
507
|
+
([current.page_struct["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])[0..8]).uniq
|
530
508
|
end
|
531
509
|
|
532
510
|
def ids_json(env)
|
533
|
-
|
534
|
-
ids = [current.page_struct["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])[0..8]
|
535
|
-
::JSON.generate(ids.uniq)
|
511
|
+
::JSON.generate(ids(env))
|
536
512
|
end
|
537
513
|
|
538
514
|
def ids_comma_separated(env)
|
539
|
-
|
540
|
-
ids = [current.page_struct["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])[0..8]
|
541
|
-
ids.join(",")
|
515
|
+
ids(env).join(",")
|
542
516
|
end
|
543
517
|
|
544
518
|
# get_profile_script returns script to be injected inside current html page
|
@@ -548,26 +522,37 @@ module Rack
|
|
548
522
|
# * you have disabled auto append behaviour throught :auto_inject => false flag
|
549
523
|
# * you do not want script to be automatically appended for the current page. You can also call cancel_auto_inject
|
550
524
|
def get_profile_script(env)
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
525
|
+
|
526
|
+
settings = {
|
527
|
+
:path => "#{env['SCRIPT_NAME']}#{@config.base_url_path}",
|
528
|
+
:version => MiniProfiler::VERSION,
|
529
|
+
:position => @config.position,
|
530
|
+
:showTrivial => false,
|
531
|
+
:showChildren => false,
|
532
|
+
:maxTracesToShow => 10,
|
533
|
+
:showControls => false,
|
534
|
+
:authorized => true,
|
535
|
+
:toggleShortcut => @config.toggle_shortcut,
|
536
|
+
:startHidden => @config.start_hidden
|
537
|
+
}
|
538
|
+
|
539
|
+
if current && current.page_struct
|
540
|
+
settings[:ids] = ids_comma_separated(env)
|
541
|
+
settings[:currentId] = current.page_struct["Id"]
|
542
|
+
else
|
543
|
+
settings[:ids] = []
|
544
|
+
settings[:currentId] = ""
|
545
|
+
end
|
546
|
+
|
563
547
|
# TODO : cache this snippet
|
564
548
|
script = IO.read(::File.expand_path('../html/profile_handler.js', ::File.dirname(__FILE__)))
|
565
549
|
# replace the variables
|
566
|
-
|
567
|
-
regex = Regexp.new("\\{#{
|
568
|
-
script.gsub!(regex,
|
550
|
+
settings.each do |k,v|
|
551
|
+
regex = Regexp.new("\\{#{k.to_s}\\}")
|
552
|
+
script.gsub!(regex, v.to_s)
|
569
553
|
end
|
570
|
-
|
554
|
+
|
555
|
+
current.inject_js = false if current
|
571
556
|
script
|
572
557
|
end
|
573
558
|
|