rack-mini-profiler 0.1.15.pre → 0.1.20

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.

Potentially problematic release.


This version of rack-mini-profiler might be problematic. Click here for more details.

data/CHANGELOG CHANGED
@@ -69,3 +69,25 @@
69
69
  * 1.15.pre
70
70
  * fixed annoying bug where client settings were not sticking
71
71
  * fixed long standing issue with Rack::ConditionalGet stopping MiniProfiler from working properly
72
+
73
+ 5-September-2012 - Sam
74
+
75
+ * 1.16
76
+ * fixed long standing problem specs (issue with memory store)
77
+ * fixed issue where profiler would be dumped when you got a 404 in production (and any time rails is bypassed)
78
+ * implemented stacktrace properly
79
+
80
+ 9-September-2012 - Sam
81
+
82
+ * 1.17
83
+ * pp=sample was bust unless stacktrace was installed
84
+
85
+ 10-September-2012 - Sam
86
+
87
+ * 1.19
88
+ * fix compat issue with 1.8.7
89
+
90
+ 12-September-2012 - Sam
91
+
92
+ * 1.20
93
+ * Added pp=profile-gc , it allows you to profile the GC in Ruby 1.9.3
@@ -138,7 +138,7 @@
138
138
  </td>
139
139
 
140
140
  {{if HasSqlTimings}}
141
- <td class="profiler-duration {{if HasDuplicateSqlTimings}}profiler-warning{{/if}}" title="{{if HasDuplicateSqlTimings}}duplicate queries detected - {{/if}}${ExecutedReaders} reader, ${ExecutedScalars} scalar, ${ExecutedNonQueries} non-query statements executed">
141
+ <td class="profiler-duration {{if HasDuplicateSqlTimings}}profiler-warning{{/if}}" title="{{if HasDuplicateSqlTimings}}duplicate queries detected - {{/if}}{{if ExecutedReaders > 0 || ExecutedScalars > 0 || ExecutedNonQueries > 0}}${ExecutedReaders} reader, ${ExecutedScalars} scalar, ${ExecutedNonQueries} non-query statements executed{{/if}}">
142
142
  <a class="profiler-queries-show">
143
143
  {{if HasDuplicateSqlTimings}}<span class="profiler-nuclear">!</span>{{/if}}
144
144
  ${SqlTimings.length} <span class="profiler-unit">sql</span>
@@ -19,7 +19,7 @@ module Rack
19
19
 
20
20
  class MiniProfiler
21
21
 
22
- VERSION = '106'.freeze
22
+ VERSION = '107'.freeze
23
23
 
24
24
  class << self
25
25
 
@@ -167,6 +167,7 @@ module Rack
167
167
 
168
168
 
169
169
  def call(env)
170
+
170
171
  client_settings = ClientSettings.new(env)
171
172
 
172
173
  status = headers = body = nil
@@ -207,6 +208,10 @@ module Rack
207
208
  return [status,headers,body]
208
209
  end
209
210
 
211
+ if query_string =~ /pp=profile-gc/
212
+ return profile_gc(env)
213
+ end
214
+
210
215
  MiniProfiler.create_current(env, @config)
211
216
  MiniProfiler.deauthorize_request if @config.authorization_mode == :whitelist
212
217
  if query_string =~ /pp=normal-backtrace/
@@ -224,17 +229,30 @@ module Rack
224
229
  done_sampling = false
225
230
  quit_sampler = false
226
231
  backtraces = nil
227
- missing_stacktrace = false
232
+ stacktrace_installed = true
228
233
  if query_string =~ /pp=sample/
234
+ skip_frames = 0
229
235
  backtraces = []
230
236
  t = Thread.current
237
+
238
+ begin
239
+ require 'stacktrace'
240
+ skip_frames = stacktrace.length
241
+ rescue LoadError
242
+ stacktrace_installed = false
243
+ end
244
+
231
245
  Thread.new {
232
246
  begin
233
247
  i = 10000 # for sanity never grab more than 10k samples
234
248
  while i > 0
235
249
  break if done_sampling
236
250
  i -= 1
237
- backtraces << t.backtrace
251
+ if stacktrace_installed
252
+ backtraces << t.stacktrace(0,-(1+skip_frames), StackFrame::Flags::METHOD | StackFrame::Flags::KLASS)
253
+ else
254
+ backtraces << t.backtrace
255
+ end
238
256
  sleep 0.001
239
257
  end
240
258
  ensure
@@ -263,10 +281,14 @@ module Rack
263
281
 
264
282
  skip_it = current.discard
265
283
  if (config.authorization_mode == :whitelist && !MiniProfiler.request_authorized?)
266
- client_settings.discard_cookie!(headers)
284
+ # this is non-obvious, don't kill the profiling cookie on errors or short requests
285
+ # this ensures that stuff that never reaches the rails stack does not kill profiling
286
+ if status == 200 && ((Time.now - start) > 0.1)
287
+ client_settings.discard_cookie!(headers)
288
+ end
267
289
  skip_it = true
268
290
  end
269
-
291
+
270
292
  return [status,headers,body] if skip_it
271
293
 
272
294
  # we must do this here, otherwise current[:discard] is not being properly treated
@@ -277,7 +299,7 @@ module Rack
277
299
 
278
300
  if query_string =~ /pp=help/
279
301
  body.close if body.respond_to? :close
280
- return help(nil, client_settings)
302
+ return help(client_settings)
281
303
  end
282
304
 
283
305
  page_struct = current.page_struct
@@ -285,7 +307,6 @@ module Rack
285
307
 
286
308
  if backtraces
287
309
  body.close if body.respond_to? :close
288
- return help(:stacktrace, client_settings) if missing_stacktrace
289
310
  return analyze(backtraces, page_struct)
290
311
  end
291
312
 
@@ -355,7 +376,7 @@ module Rack
355
376
  [200, headers, [body]]
356
377
  end
357
378
 
358
- def help(category = nil, client_settings)
379
+ def help(client_settings)
359
380
  headers = {'Content-Type' => 'text/plain'}
360
381
  body = "Append the following to your query string:
361
382
 
@@ -365,18 +386,73 @@ module Rack
365
386
  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)
366
387
  pp=normal-backtrace #{"(*) " if client_settings.backtrace_default?}: collect stack traces from all the SQL executed and filter normally
367
388
  pp=full-backtrace #{"(*) " if client_settings.backtrace_full?}: enable full backtraces for SQL executed (use pp=normal-backtrace to disable)
368
- pp=sample : sample stack traces and return a report isolating heavy usage (experimental)
389
+ pp=sample : sample stack traces and return a report isolating heavy usage (experimental works best with the stacktrace gem)
369
390
  pp=disable : disable profiling for this session
370
391
  pp=enable : enable profiling for this session (if previously disabled)
392
+ pp=profile-gc: perform gc profiling on this request (ruby 1.9.3 only)
371
393
  "
372
- if (category == :stacktrace)
373
- body = "pp=stacktrace requires the stacktrace gem - add gem 'stacktrace' to your Gemfile"
374
- end
375
394
 
376
395
  client_settings.write!(headers)
377
396
  [200, headers, [body]]
378
397
  end
379
398
 
399
+
400
+ def object_space_stats
401
+ stats = {}
402
+ ObjectSpace.each_object { |o|
403
+ stats[o.class] ||= 1
404
+ stats[o.class] += 1
405
+ }
406
+ stats
407
+ end
408
+
409
+ def diff_object_stats(before,after)
410
+ diff = {}
411
+ after.each do |k,v|
412
+ diff[k] = v - (before[k] || 0)
413
+ end
414
+ before.each do |k,v|
415
+ diff[k] = 0 - v unless after[k]
416
+ end
417
+
418
+ diff
419
+ end
420
+
421
+ def profile_gc(env)
422
+
423
+ body = "";
424
+
425
+ stat_before = object_space_stats
426
+ begin
427
+ GC::Profiler.clear
428
+ GC::Profiler.enable
429
+ @app.call(env)
430
+ body << GC::Profiler.result
431
+ ensure
432
+ GC::Profiler.disable
433
+ end
434
+ stat_after = object_space_stats
435
+
436
+ diff = diff_object_stats(stat_before,stat_after)
437
+
438
+ body << "
439
+ ObjectSpace delta caused by request:
440
+ --------------------------------------------\n"
441
+ diff.to_a.reject{|k,v| v == 0}.sort{|x,y| y[1] <=> x[1]}.each do |k,v|
442
+ body << "#{k} : #{v}\n" if v != 0
443
+ end
444
+
445
+ body << "\n
446
+ ObjectSpace stats:
447
+ -----------------\n"
448
+
449
+ stat_after.to_a.sort{|x,y| y[1] <=> x[1]}.each do |k,v|
450
+ body << "#{k} : #{v}\n"
451
+ end
452
+
453
+ return [200, {'Content-Type' => 'text/plain'}, body]
454
+ end
455
+
380
456
  def analyze(traces, page_struct)
381
457
  headers = {'Content-Type' => 'text/plain'}
382
458
  body = "Collected: #{traces.count} stack traces. Duration(ms): #{page_struct.duration_ms}"
@@ -387,6 +463,7 @@ module Rack
387
463
  fulldump << "\n\n"
388
464
  distinct = {}
389
465
  trace.each do |frame|
466
+ frame = "#{frame.klass}::#{frame.method}" unless String === frame
390
467
  unless distinct[frame]
391
468
  distinct[frame] = true
392
469
  seen[frame] ||= 0
@@ -8,6 +8,22 @@ module Rack
8
8
  c.current_timer.add_sql(query, elapsed_ms, c.page_struct, c.skip_backtrace, c.full_backtrace) if (c && c.current_timer)
9
9
  end
10
10
 
11
+ def start_step(name)
12
+ if current
13
+ parent_timer = current.current_timer
14
+ current.current_timer = current_timer = current.current_timer.add_child(name)
15
+ [current_timer,parent_timer]
16
+ end
17
+ end
18
+
19
+ def finish_step(obj)
20
+ if obj && current
21
+ current_timer, parent_timer = obj
22
+ current_timer.record_time
23
+ current.current_timer = parent_timer
24
+ end
25
+ end
26
+
11
27
  # perform a profiling step on given block
12
28
  def step(name, opts = nil)
13
29
  if current
@@ -26,8 +42,11 @@ module Rack
26
42
  end
27
43
 
28
44
  def unprofile_method(klass, method)
29
- with_profiling = (method.to_s + "_with_mini_profiler").intern
30
- without_profiling = (method.to_s + "_without_mini_profiler").intern
45
+
46
+ clean = clean_method_name(method)
47
+
48
+ with_profiling = ("#{clean}_with_mini_profiler").intern
49
+ without_profiling = ("#{clean}_without_mini_profiler").intern
31
50
 
32
51
  if klass.send :method_defined?, with_profiling
33
52
  klass.send :alias_method, method, without_profiling
@@ -38,8 +57,10 @@ module Rack
38
57
 
39
58
  def profile_method(klass, method, &blk)
40
59
  default_name = klass.to_s + " " + method.to_s
41
- with_profiling = (method.to_s + "_with_mini_profiler").intern
42
- without_profiling = (method.to_s + "_without_mini_profiler").intern
60
+ clean = clean_method_name(method)
61
+
62
+ with_profiling = ("#{clean}_with_mini_profiler").intern
63
+ without_profiling = ("#{clean}_without_mini_profiler").intern
43
64
 
44
65
  if klass.send :method_defined?, with_profiling
45
66
  return # dont double profile
@@ -67,6 +88,13 @@ module Rack
67
88
  end
68
89
  klass.send :alias_method, method, with_profiling
69
90
  end
91
+
92
+ private
93
+
94
+ def clean_method_name(method)
95
+ method.to_s.gsub(/[\?\!]/, "")
96
+ end
97
+
70
98
  end
71
99
  end
72
100
  end
@@ -55,7 +55,7 @@ module Rack
55
55
  def cleanup_cache
56
56
  expire_older_than = ((Time.now.to_f - MiniProfiler::MemoryStore::EXPIRE_TIMER_CACHE) * 1000).to_i
57
57
  @timer_struct_lock.synchronize {
58
- @timer_struct_cache.delete_if { |k, v| v['Root']['StartMilliseconds'] < expire_older_than }
58
+ @timer_struct_cache.delete_if { |k, v| v['Started'] < expire_older_than }
59
59
  }
60
60
  end
61
61
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rack-mini-profiler"
3
- s.version = "0.1.15.pre"
3
+ s.version = "0.1.20"
4
4
  s.summary = "Profiles loading speed for rack applications."
5
5
  s.authors = ["Aleks Totic","Sam Saffron", "Robin Ward"]
6
6
  s.description = "Page loading speed displayed on every page. Optimize while you develop, performance is a feature."
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-mini-profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.15.pre
5
- prerelease: 7
4
+ version: 0.1.20
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Aleks Totic
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-09-04 00:00:00.000000000 Z
14
+ date: 2012-09-11 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rack
@@ -131,13 +131,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
131
131
  version: '0'
132
132
  segments:
133
133
  - 0
134
- hash: 439688077
134
+ hash: -250357363
135
135
  required_rubygems_version: !ruby/object:Gem::Requirement
136
136
  none: false
137
137
  requirements:
138
- - - ! '>'
138
+ - - ! '>='
139
139
  - !ruby/object:Gem::Version
140
- version: 1.3.1
140
+ version: '0'
141
+ segments:
142
+ - 0
143
+ hash: -250357363
141
144
  requirements: []
142
145
  rubyforge_project:
143
146
  rubygems_version: 1.8.24