rack-mini-profiler 0.1.31 → 0.9.3

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +149 -0
  3. data/README.md +285 -0
  4. data/{Ruby/lib → lib}/html/includes.css +15 -4
  5. data/{Ruby/lib → lib}/html/includes.js +95 -59
  6. data/{Ruby/lib → lib}/html/includes.less +21 -5
  7. data/{Ruby/lib → lib}/html/includes.tmpl +50 -50
  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/lib/html/list.tmpl +34 -0
  13. data/lib/html/profile_handler.js +1 -0
  14. data/{Ruby/lib → lib}/html/share.html +2 -2
  15. data/lib/mini_profiler/asset_version.rb +5 -0
  16. data/{Ruby/lib → lib}/mini_profiler/client_settings.rb +3 -3
  17. data/lib/mini_profiler/config.rb +65 -0
  18. data/{Ruby/lib → lib}/mini_profiler/context.rb +0 -0
  19. data/lib/mini_profiler/gc_profiler.rb +181 -0
  20. data/{Ruby/lib → lib}/mini_profiler/profiler.rb +120 -96
  21. data/{Ruby/lib → lib}/mini_profiler/profiling_methods.rb +15 -17
  22. data/{Ruby/lib → lib}/mini_profiler/storage/abstract_store.rb +0 -0
  23. data/{Ruby/lib → lib}/mini_profiler/storage/file_store.rb +30 -8
  24. data/{Ruby/lib → lib}/mini_profiler/storage/memcache_store.rb +5 -7
  25. data/lib/mini_profiler/storage/memory_store.rb +115 -0
  26. data/{Ruby/lib → lib}/mini_profiler/storage/redis_store.rb +19 -11
  27. data/lib/mini_profiler/timer_struct/base.rb +33 -0
  28. data/lib/mini_profiler/timer_struct/client.rb +89 -0
  29. data/lib/mini_profiler/timer_struct/custom.rb +22 -0
  30. data/lib/mini_profiler/timer_struct/page.rb +62 -0
  31. data/lib/mini_profiler/timer_struct/request.rb +126 -0
  32. data/lib/mini_profiler/timer_struct/sql.rb +59 -0
  33. data/lib/mini_profiler/version.rb +5 -0
  34. data/{Ruby/lib → lib}/mini_profiler_rails/railtie.rb +23 -6
  35. data/lib/patches/db/activerecord.rb +42 -0
  36. data/lib/patches/db/moped.rb +12 -0
  37. data/lib/patches/db/mysql2.rb +30 -0
  38. data/lib/patches/db/pg.rb +104 -0
  39. data/lib/patches/db/plucky.rb +47 -0
  40. data/lib/patches/db/rsolr.rb +24 -0
  41. data/lib/patches/db/sequel.rb +10 -0
  42. data/{Ruby/lib → lib}/patches/net_patches.rb +0 -0
  43. data/lib/patches/sql_patches.rb +46 -0
  44. data/lib/rack-mini-profiler.rb +35 -0
  45. data/rack-mini-profiler.gemspec +28 -16
  46. metadata +171 -52
  47. data/Ruby/CHANGELOG +0 -161
  48. data/Ruby/README.md +0 -172
  49. data/Ruby/lib/html/list.tmpl +0 -34
  50. data/Ruby/lib/html/profile_handler.js +0 -1
  51. data/Ruby/lib/mini_profiler/client_timer_struct.rb +0 -78
  52. data/Ruby/lib/mini_profiler/config.rb +0 -58
  53. data/Ruby/lib/mini_profiler/custom_timer_struct.rb +0 -22
  54. data/Ruby/lib/mini_profiler/gc_profiler.rb +0 -107
  55. data/Ruby/lib/mini_profiler/gc_profiler_ruby_head.rb +0 -40
  56. data/Ruby/lib/mini_profiler/page_timer_struct.rb +0 -58
  57. data/Ruby/lib/mini_profiler/request_timer_struct.rb +0 -115
  58. data/Ruby/lib/mini_profiler/sql_timer_struct.rb +0 -58
  59. data/Ruby/lib/mini_profiler/storage/memory_store.rb +0 -65
  60. data/Ruby/lib/mini_profiler/timer_struct.rb +0 -33
  61. data/Ruby/lib/mini_profiler/version.rb +0 -5
  62. data/Ruby/lib/patches/sql_patches.rb +0 -277
  63. data/Ruby/lib/rack-mini-profiler.rb +0 -7
@@ -3,32 +3,30 @@ module Rack
3
3
  module ProfilingMethods
4
4
 
5
5
  def record_sql(query, elapsed_ms)
6
+ return unless current
6
7
  c = current
7
- return unless c
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
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
12
+ return unless current
13
+ parent_timer = current.current_timer
14
+ current.current_timer = current_timer = current.current_timer.add_child(name)
15
+ [current_timer,parent_timer]
17
16
  end
18
17
 
19
18
  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
19
+ return unless obj && current
20
+ current_timer, parent_timer = obj
21
+ current_timer.record_time
22
+ current.current_timer = parent_timer
25
23
  end
26
24
 
27
25
  # perform a profiling step on given block
28
26
  def step(name, opts = nil)
29
27
  if current
30
- parent_timer = current.current_timer
31
- result = nil
28
+ parent_timer = current.current_timer
29
+ result = nil
32
30
  current.current_timer = current_timer = current.current_timer.add_child(name)
33
31
  begin
34
32
  result = yield if block_given?
@@ -65,9 +63,9 @@ module Rack
65
63
 
66
64
  def profile_method(klass, method, type = :profile, &blk)
67
65
  default_name = type==:counter ? method.to_s : klass.to_s + " " + method.to_s
68
- clean = clean_method_name(method)
66
+ clean = clean_method_name(method)
69
67
 
70
- with_profiling = ("#{clean}_with_mini_profiler").intern
68
+ with_profiling = ("#{clean}_with_mini_profiler").intern
71
69
  without_profiling = ("#{clean}_without_mini_profiler").intern
72
70
 
73
71
  if klass.send :method_defined?, with_profiling
@@ -131,8 +129,8 @@ module Rack
131
129
  def counter(type, duration_ms=nil)
132
130
  result = nil
133
131
  if block_given?
134
- start = Time.now
135
- result = yield
132
+ start = Time.now
133
+ result = yield
136
134
  duration_ms = (Time.now - start).to_f * 1000
137
135
  end
138
136
  return result if current.nil? || !request_authorized?
@@ -2,6 +2,10 @@ module Rack
2
2
  class MiniProfiler
3
3
  class FileStore < AbstractStore
4
4
 
5
+ # Sub-class thread so we have a named thread (useful for debugging in Thread.list).
6
+ class CacheCleanupThread < Thread
7
+ end
8
+
5
9
  class FileCache
6
10
  def initialize(path, prefix)
7
11
  @path = path
@@ -35,27 +39,45 @@ module Rack
35
39
  @expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
36
40
  raise ArgumentError.new :path unless @path
37
41
  @timer_struct_cache = FileCache.new(@path, "mp_timers")
38
- @timer_struct_lock = Mutex.new
39
- @user_view_cache = FileCache.new(@path, "mp_views")
40
- @user_view_lock = Mutex.new
42
+ @timer_struct_lock = Mutex.new
43
+ @user_view_cache = FileCache.new(@path, "mp_views")
44
+ @user_view_lock = Mutex.new
41
45
 
42
46
  me = self
43
- Thread.new do
47
+ t = CacheCleanupThread.new do
48
+ interval = 10
49
+ cleanup_cache_cycle = 3600
50
+ cycle_count = 1
51
+
44
52
  begin
45
- while true do
53
+ until Thread.current[:should_exit] do
46
54
  # TODO: a sane retry count before bailing
47
- me.cleanup_cache
48
- sleep(3600)
55
+
56
+ # We don't want to hit the filesystem every 10s to clean up the cache so we need to do a bit of
57
+ # accounting to avoid sleeping that entire time. We don't want to sleep for the entire period because
58
+ # it means the thread will stay live in hot deployment scenarios, keeping a potentially large memory
59
+ # graph from being garbage collected upon undeploy.
60
+ if cycle_count * interval >= cleanup_cache_cycle
61
+ cycle_count = 1
62
+ me.cleanup_cache
63
+ end
64
+
65
+ sleep(interval)
66
+ cycle_count += 1
49
67
  end
50
68
  rescue
51
69
  # don't crash the thread, we can clean up next time
52
70
  end
53
71
  end
72
+
73
+ at_exit { t[:should_exit] = true }
74
+
75
+ t
54
76
  end
55
77
 
56
78
  def save(page_struct)
57
79
  @timer_struct_lock.synchronize {
58
- @timer_struct_cache[page_struct['Id']] = page_struct
80
+ @timer_struct_cache[page_struct[:id]] = page_struct
59
81
  }
60
82
  end
61
83
 
@@ -3,25 +3,23 @@ module Rack
3
3
  class MemcacheStore < AbstractStore
4
4
 
5
5
  EXPIRES_IN_SECONDS = 60 * 60 * 24
6
- MAX_RETRIES = 10
6
+ MAX_RETRIES = 10
7
7
 
8
8
  def initialize(args = nil)
9
9
  require 'dalli' unless defined? Dalli
10
10
  args ||= {}
11
- @prefix = args[:prefix] || "MPMemcacheStore"
12
- @client = args[:client] || Dalli::Client.new
11
+ @prefix = args[:prefix] || "MPMemcacheStore"
12
+ @client = args[:client] || Dalli::Client.new
13
13
  @expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
14
14
  end
15
15
 
16
16
  def save(page_struct)
17
- @client.set("#{@prefix}#{page_struct['Id']}", Marshal::dump(page_struct), @expires_in_seconds)
17
+ @client.set("#{@prefix}#{page_struct[:id]}", Marshal::dump(page_struct), @expires_in_seconds)
18
18
  end
19
19
 
20
20
  def load(id)
21
21
  raw = @client.get("#{@prefix}#{id}")
22
- if raw
23
- Marshal::load raw
24
- end
22
+ Marshal::load(raw) if raw
25
23
  end
26
24
 
27
25
  def set_unviewed(user, id)
@@ -0,0 +1,115 @@
1
+ module Rack
2
+ class MiniProfiler
3
+ class MemoryStore < AbstractStore
4
+
5
+ # Sub-class thread so we have a named thread (useful for debugging in Thread.list).
6
+ class CacheCleanupThread < Thread
7
+
8
+ def initialize(interval, cycle, store)
9
+ super
10
+ @store = store
11
+ @interval = interval
12
+ @cycle = cycle
13
+ @cycle_count = 1
14
+ end
15
+
16
+ def should_cleanup?
17
+ @cycle_count * @interval >= @cycle
18
+ end
19
+
20
+
21
+ # We don't want to hit the filesystem every 10s to clean up the cache so we need to do a bit of
22
+ # accounting to avoid sleeping that entire time. We don't want to sleep for the entire period because
23
+ # it means the thread will stay live in hot deployment scenarios, keeping a potentially large memory
24
+ # graph from being garbage collected upon undeploy.
25
+ def sleepy_run
26
+ cleanup if should_cleanup?
27
+ sleep(@interval)
28
+ increment_cycle
29
+ end
30
+
31
+ def cleanup
32
+ @store.cleanup_cache
33
+ @cycle_count = 1
34
+ end
35
+
36
+ def cycle_count
37
+ @cycle_count
38
+ end
39
+
40
+ def increment_cycle
41
+ @cycle_count += 1
42
+ end
43
+ end
44
+
45
+ EXPIRES_IN_SECONDS = 60 * 60 * 24
46
+ CLEANUP_INTERVAL = 10
47
+ CLEANUP_CYCLE = 3600
48
+
49
+ def initialize(args = nil)
50
+ args ||= {}
51
+ @expires_in_seconds = args.fetch(:expires_in) { EXPIRES_IN_SECONDS }
52
+ initialize_locks
53
+ initialize_cleanup_thread(args)
54
+ end
55
+
56
+ def initialize_locks
57
+ @timer_struct_lock = Mutex.new
58
+ @user_view_lock = Mutex.new
59
+ @timer_struct_cache = {}
60
+ @user_view_cache = {}
61
+ end
62
+
63
+ #FIXME: use weak ref, trouble it may be broken in 1.9 so need to use the 'ref' gem
64
+ def initialize_cleanup_thread(args={})
65
+ cleanup_interval = args.fetch(:cleanup_interval) { CLEANUP_INTERVAL }
66
+ cleanup_cycle = args.fetch(:cleanup_cycle) { CLEANUP_CYCLE }
67
+ t = CacheCleanupThread.new(cleanup_interval, cleanup_cycle, self) do |t|
68
+ until Thread.current[:should_exit] do
69
+ self.sleepy_run
70
+ end
71
+ end
72
+ at_exit { t[:should_exit] = true }
73
+ end
74
+
75
+ def save(page_struct)
76
+ @timer_struct_lock.synchronize {
77
+ @timer_struct_cache[page_struct[:id]] = page_struct
78
+ }
79
+ end
80
+
81
+ def load(id)
82
+ @timer_struct_lock.synchronize {
83
+ @timer_struct_cache[id]
84
+ }
85
+ end
86
+
87
+ def set_unviewed(user, id)
88
+ @user_view_lock.synchronize {
89
+ @user_view_cache[user] ||= []
90
+ @user_view_cache[user] << id
91
+ }
92
+ end
93
+
94
+ def set_viewed(user, id)
95
+ @user_view_lock.synchronize {
96
+ @user_view_cache[user] ||= []
97
+ @user_view_cache[user].delete(id)
98
+ }
99
+ end
100
+
101
+ def get_unviewed_ids(user)
102
+ @user_view_lock.synchronize {
103
+ @user_view_cache[user]
104
+ }
105
+ end
106
+
107
+ def cleanup_cache
108
+ expire_older_than = ((Time.now.to_f - @expires_in_seconds) * 1000).to_i
109
+ @timer_struct_lock.synchronize {
110
+ @timer_struct_cache.delete_if { |k, v| v[:started] < expire_older_than }
111
+ }
112
+ end
113
+ end
114
+ end
115
+ end
@@ -5,25 +5,32 @@ module Rack
5
5
  EXPIRES_IN_SECONDS = 60 * 60 * 24
6
6
 
7
7
  def initialize(args = nil)
8
- @args = args || {}
9
- @prefix = @args.delete(:prefix) || 'MPRedisStore'
10
- @redis_connection = @args.delete(:connection)
8
+ @args = args || {}
9
+ @prefix = @args.delete(:prefix) || 'MPRedisStore'
10
+ @redis_connection = @args.delete(:connection)
11
11
  @expires_in_seconds = @args.delete(:expires_in) || EXPIRES_IN_SECONDS
12
12
  end
13
13
 
14
14
  def save(page_struct)
15
- redis.setex "#{@prefix}#{page_struct['Id']}", @expires_in_seconds, Marshal::dump(page_struct)
15
+ redis.setex "#{@prefix}#{page_struct[:id]}", @expires_in_seconds, Marshal::dump(page_struct)
16
16
  end
17
17
 
18
18
  def load(id)
19
- raw = redis.get "#{@prefix}#{id}"
20
- if raw
21
- Marshal::load raw
19
+ key = "#{@prefix}#{id}"
20
+ raw = redis.get key
21
+ begin
22
+ Marshal::load(raw) if raw
23
+ rescue
24
+ # bad format, junk old data
25
+ redis.del key
26
+ nil
22
27
  end
23
28
  end
24
29
 
25
30
  def set_unviewed(user, id)
26
- redis.sadd "#{@prefix}-#{user}-v", id
31
+ key = "#{@prefix}-#{user}-v"
32
+ redis.sadd key, id
33
+ redis.expire key, @expires_in_seconds
27
34
  end
28
35
 
29
36
  def set_viewed(user, id)
@@ -44,9 +51,10 @@ unviewed_ids: #{get_unviewed_ids(user)}
44
51
  private
45
52
 
46
53
  def redis
47
- return @redis_connection if @redis_connection
48
- require 'redis' unless defined? Redis
49
- @redis_connection ||= Redis.new @args
54
+ @redis_connection ||= begin
55
+ require 'redis' unless defined? Redis
56
+ Redis.new(@args)
57
+ end
50
58
  end
51
59
 
52
60
  end
@@ -0,0 +1,33 @@
1
+ module Rack
2
+ class MiniProfiler
3
+ module TimerStruct
4
+ # A base class for timing structures
5
+ class Base
6
+
7
+ def initialize(attrs={})
8
+ @attributes = attrs
9
+ end
10
+
11
+ def attributes
12
+ @attributes ||= {}
13
+ end
14
+
15
+ def [](name)
16
+ attributes[name]
17
+ end
18
+
19
+ def []=(name, val)
20
+ attributes[name] = val
21
+ self
22
+ end
23
+
24
+ def to_json(*a)
25
+ # this does could take in an option hash, but the only interesting there is max_nesting.
26
+ # if this becomes an option we could increase
27
+ ::JSON.generate( @attributes, :max_nesting => 100 )
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,89 @@
1
+ module Rack
2
+ class MiniProfiler
3
+ module TimerStruct
4
+
5
+ # This class holds the client timings
6
+ class Client < TimerStruct::Base
7
+
8
+ def self.init_instrumentation
9
+ %Q{
10
+ <script type="text/javascript">
11
+ mPt=function(){var t=[];return{t:t,probe:function(n){t.push({d:new Date(),n:n})}}}()
12
+ </script>
13
+ }
14
+ end
15
+
16
+ # used by Railtie to instrument asset_tag for JS / CSS
17
+ def self.instrument(name, orig)
18
+ probe = "<script>mPt.probe('#{name}')</script>"
19
+ wrapped = probe
20
+ wrapped << orig
21
+ wrapped << probe
22
+ wrapped
23
+ end
24
+
25
+
26
+ def initialize(env={})
27
+ super
28
+ end
29
+
30
+ def redirect_count
31
+ self[:redirect_count]
32
+ end
33
+
34
+ def timings
35
+ self[:timings]
36
+ end
37
+
38
+ def self.init_from_form_data(env, page_struct)
39
+ timings = []
40
+ clientTimes, clientPerf, baseTime = nil
41
+ form = env['rack.request.form_hash']
42
+
43
+ clientPerf = form['clientPerformance'] if form
44
+ clientTimes = clientPerf['timing'] if clientPerf
45
+ baseTime = clientTimes['navigationStart'].to_i if clientTimes
46
+ return unless clientTimes && baseTime
47
+
48
+ probes = form['clientProbes']
49
+ translated = {}
50
+ if probes && !["null", ""].include?(probes)
51
+ probes.each do |id, val|
52
+ name = val["n"]
53
+ translated[name] ||= {}
54
+ if translated[name][:start]
55
+ translated[name][:finish] = val["d"]
56
+ else
57
+ translated[name][:start] = val["d"]
58
+ end
59
+ end
60
+ end
61
+
62
+ translated.each do |name, data|
63
+ h = {"Name" => name, "Start" => data[:start].to_i - baseTime}
64
+ h["Duration"] = data[:finish].to_i - data[:start].to_i if data[:finish]
65
+ timings.push(h)
66
+ end
67
+
68
+ clientTimes.keys.find_all{|k| k =~ /Start$/ }.each do |k|
69
+ start = clientTimes[k].to_i - baseTime
70
+ finish = clientTimes[k.sub(/Start$/, "End")].to_i - baseTime
71
+ duration = 0
72
+ duration = finish - start if finish > start
73
+ name = k.sub(/Start$/, "").split(/(?=[A-Z])/).map{|s| s.capitalize}.join(' ')
74
+ timings.push({"Name" => name, "Start" => start, "Duration" => duration}) if start >= 0
75
+ end
76
+
77
+ clientTimes.keys.find_all{|k| !(k =~ /(End|Start)$/)}.each do |k|
78
+ timings.push("Name" => k, "Start" => clientTimes[k].to_i - baseTime, "Duration" => -1)
79
+ end
80
+
81
+ TimerStruct::Client.new.tap do |rval|
82
+ rval[:redirect_count] = env['rack.request.form_hash']['clientPerformance']['navigation']['redirect_count']
83
+ rval[:timings] = timings
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end