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.

Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +149 -0
  3. data/README.md +271 -0
  4. data/{Ruby/lib → lib}/html/includes.css +0 -0
  5. data/{Ruby/lib → lib}/html/includes.js +9 -8
  6. data/{Ruby/lib → lib}/html/includes.less +0 -0
  7. data/{Ruby/lib → lib}/html/includes.tmpl +1 -1
  8. data/{Ruby/lib → lib}/html/jquery.1.7.1.js +0 -0
  9. data/{Ruby/lib → lib}/html/jquery.tmpl.js +0 -0
  10. data/{Ruby/lib → lib}/html/list.css +2 -2
  11. data/{Ruby/lib → lib}/html/list.js +1 -1
  12. data/{Ruby/lib → lib}/html/list.tmpl +2 -2
  13. data/lib/html/profile_handler.js +1 -0
  14. data/{Ruby/lib → lib}/html/share.html +2 -2
  15. data/{Ruby/lib → lib}/mini_profiler/client_settings.rb +0 -0
  16. data/{Ruby/lib → lib}/mini_profiler/client_timer_struct.rb +0 -0
  17. data/{Ruby/lib → lib}/mini_profiler/config.rb +11 -4
  18. data/{Ruby/lib → lib}/mini_profiler/context.rb +1 -1
  19. data/{Ruby/lib → lib}/mini_profiler/custom_timer_struct.rb +0 -0
  20. data/lib/mini_profiler/gc_profiler.rb +181 -0
  21. data/{Ruby/lib → lib}/mini_profiler/page_timer_struct.rb +0 -0
  22. data/{Ruby/lib → lib}/mini_profiler/profiler.rb +108 -123
  23. data/{Ruby/lib → lib}/mini_profiler/profiling_methods.rb +0 -0
  24. data/{Ruby/lib → lib}/mini_profiler/request_timer_struct.rb +0 -0
  25. data/{Ruby/lib → lib}/mini_profiler/sql_timer_struct.rb +0 -0
  26. data/{Ruby/lib → lib}/mini_profiler/storage/abstract_store.rb +0 -0
  27. data/{Ruby/lib → lib}/mini_profiler/storage/file_store.rb +26 -4
  28. data/{Ruby/lib → lib}/mini_profiler/storage/memcache_store.rb +0 -0
  29. data/{Ruby/lib → lib}/mini_profiler/storage/memory_store.rb +25 -4
  30. data/{Ruby/lib → lib}/mini_profiler/storage/redis_store.rb +0 -0
  31. data/{Ruby/lib → lib}/mini_profiler/timer_struct.rb +0 -0
  32. data/lib/mini_profiler/version.rb +5 -0
  33. data/{Ruby/lib → lib}/mini_profiler_rails/railtie.rb +23 -6
  34. data/{Ruby/lib → lib}/patches/net_patches.rb +0 -0
  35. data/{Ruby/lib → lib}/patches/sql_patches.rb +15 -8
  36. data/{Ruby/lib → lib}/rack-mini-profiler.rb +0 -0
  37. data/rack-mini-profiler.gemspec +24 -16
  38. metadata +163 -53
  39. data/Ruby/CHANGELOG +0 -156
  40. data/Ruby/README.md +0 -172
  41. data/Ruby/lib/html/flamegraph.html +0 -351
  42. data/Ruby/lib/html/profile_handler.js +0 -1
  43. data/Ruby/lib/mini_profiler/flame_graph.rb +0 -54
  44. data/Ruby/lib/mini_profiler/gc_profiler.rb +0 -107
  45. data/Ruby/lib/mini_profiler/version.rb +0 -5
File without changes
@@ -1,4 +1,4 @@
1
- <script id="profilerTemplate" type="text/x-jquery-tmpl">
1
+ <script id="profilerTemplate" type="text/x-jquery-tmpl">
2
2
 
3
3
  <div class="profiler-result">
4
4
 
File without changes
File without changes
@@ -1,4 +1,4 @@
1
- tbody tr:nth-child(odd) { background-color:#eee; }
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;}
@@ -1,4 +1,4 @@
1
- var MiniProfiler = MiniProfiler || {};
1
+ var MiniProfiler = MiniProfiler || {};
2
2
  MiniProfiler.list = {
3
3
  init:
4
4
  function (options) {
@@ -1,4 +1,4 @@
1
- <script id="tableTemplate" type="text/x-jquery-tmpl">
1
+ <script id="tableTemplate" type="text/x-jquery-tmpl">
2
2
  <table>
3
3
  <thead>
4
4
  <tr>
@@ -31,4 +31,4 @@
31
31
  <td colspan="3"></td>
32
32
  {{/if}}
33
33
  </tr>
34
- </script>
34
+ </script>
@@ -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
- <html>
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>
@@ -12,10 +12,10 @@ module Rack
12
12
  @attributes
13
13
  end
14
14
 
15
- attr_accessor :auto_inject, :base_url_path, :pre_authorize_cb, :position,
16
- :backtrace_remove, :backtrace_includes, :backtrace_ignores, :skip_schema_queries,
17
- :storage, :user_provider, :storage_instance, :storage_options, :skip_paths, :authorization_mode,
18
- :toggle_shortcut, :start_hidden, :backtrace_threshold_ms
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
@@ -1,6 +1,6 @@
1
1
  class Rack::MiniProfiler::Context
2
2
  attr_accessor :inject_js,:current_timer,:page_struct,:skip_backtrace,:full_backtrace,:discard, :mpt_init, :measure
3
-
3
+
4
4
  def initialize(opts = {})
5
5
  opts["measure"] = true unless opts.key? "measure"
6
6
  opts.each do |k,v|
@@ -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
@@ -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
- require 'mini_profiler/flame_graph'
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
- return [404, {}, ["Request not found: #{request['id']} - user #{user(env)}"]]
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
- done_sampling = false
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
- status,headers,body = @app.call(env)
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 backtraces
340
+ if flamegraph
335
341
  body.close if body.respond_to? :close
336
- if query_string =~ /pp=sample/
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
- # no matter what it is, it should be unviewed, otherwise we will miss POST
345
- @storage.set_unviewed(page_struct['User'], page_struct['Id'])
346
- @storage.save(page_struct)
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
- result = inject_profiler(env,status,headers,body)
353
- return result if result
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>. Just append the script.
413
+ # implicit </body> and </html>. Don't do anything.
407
414
 
408
- return fragment + script
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=flamegraph: works best on Ruby 2.0, a graph representing sampled activity.
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 flame_graph(traces, page_struct)
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 analyze(traces, page_struct)
500
- headers = {'Content-Type' => 'text/plain'}
501
- body = "Collected: #{traces.count} stack traces. Duration(ms): #{page_struct.duration_ms}"
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
- # cap at 10 ids, otherwise there is a chance you can blow the header
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
- # cap at 10 ids, otherwise there is a chance you can blow the header
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
- ids = ids_comma_separated(env)
552
- path = "#{env['SCRIPT_NAME']}#{@config.base_url_path}"
553
- version = MiniProfiler::VERSION
554
- position = @config.position
555
- showTrivial = false
556
- showChildren = false
557
- maxTracesToShow = 10
558
- showControls = false
559
- currentId = current.page_struct["Id"]
560
- authorized = true
561
- toggleShortcut = @config.toggle_shortcut
562
- startHidden = @config.start_hidden
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
- [:ids, :path, :version, :position, :showTrivial, :showChildren, :maxTracesToShow, :showControls, :currentId, :authorized, :toggleShortcut, :startHidden].each do |v|
567
- regex = Regexp.new("\\{#{v.to_s}\\}")
568
- script.gsub!(regex, eval(v.to_s).to_s)
550
+ settings.each do |k,v|
551
+ regex = Regexp.new("\\{#{k.to_s}\\}")
552
+ script.gsub!(regex, v.to_s)
569
553
  end
570
- current.inject_js = false
554
+
555
+ current.inject_js = false if current
571
556
  script
572
557
  end
573
558