rack-mini-profiler 0.1.20 → 0.1.25
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.
- checksums.yaml +7 -0
- data/Ruby/CHANGELOG +130 -0
- data/{README.md → Ruby/README.md} +40 -9
- data/Ruby/lib/html/flamegraph.html +325 -0
- data/Ruby/lib/html/includes.css +451 -0
- data/{lib → Ruby/lib}/html/includes.js +135 -24
- data/{lib → Ruby/lib}/html/includes.less +38 -35
- data/{lib → Ruby/lib}/html/includes.tmpl +40 -15
- data/{lib → Ruby/lib}/html/jquery.1.7.1.js +1 -1
- data/{lib → Ruby/lib}/html/jquery.tmpl.js +1 -1
- data/{lib → Ruby/lib}/html/list.css +0 -0
- data/{lib → Ruby/lib}/html/list.js +7 -6
- data/{lib → Ruby/lib}/html/list.tmpl +0 -0
- data/Ruby/lib/html/profile_handler.js +1 -0
- data/{lib → Ruby/lib}/html/share.html +0 -0
- data/{lib → Ruby/lib}/mini_profiler/client_settings.rb +0 -0
- data/{lib → Ruby/lib}/mini_profiler/client_timer_struct.rb +1 -1
- data/{lib → Ruby/lib}/mini_profiler/config.rb +57 -52
- data/{lib → Ruby/lib}/mini_profiler/context.rb +11 -10
- data/Ruby/lib/mini_profiler/custom_timer_struct.rb +22 -0
- data/Ruby/lib/mini_profiler/flame_graph.rb +54 -0
- data/Ruby/lib/mini_profiler/gc_profiler.rb +107 -0
- data/{lib → Ruby/lib}/mini_profiler/page_timer_struct.rb +7 -2
- data/{lib → Ruby/lib}/mini_profiler/profiler.rb +206 -196
- data/{lib → Ruby/lib}/mini_profiler/profiling_methods.rb +131 -100
- data/{lib → Ruby/lib}/mini_profiler/request_timer_struct.rb +20 -1
- data/{lib → Ruby/lib}/mini_profiler/sql_timer_struct.rb +0 -0
- data/{lib → Ruby/lib}/mini_profiler/storage/abstract_store.rb +31 -27
- data/{lib → Ruby/lib}/mini_profiler/storage/file_store.rb +111 -109
- data/Ruby/lib/mini_profiler/storage/memcache_store.rb +53 -0
- data/{lib → Ruby/lib}/mini_profiler/storage/memory_store.rb +65 -63
- data/Ruby/lib/mini_profiler/storage/redis_store.rb +54 -0
- data/{lib → Ruby/lib}/mini_profiler/timer_struct.rb +0 -0
- data/Ruby/lib/mini_profiler/version.rb +5 -0
- data/{lib → Ruby/lib}/mini_profiler_rails/railtie.rb +3 -2
- data/Ruby/lib/patches/net_patches.rb +14 -0
- data/{lib → Ruby/lib}/patches/sql_patches.rb +89 -48
- data/{lib → Ruby/lib}/rack-mini-profiler.rb +2 -1
- data/rack-mini-profiler.gemspec +8 -6
- metadata +56 -65
- data/CHANGELOG +0 -93
- data/lib/html/includes.css +0 -75
- data/lib/html/profile_handler.js +0 -62
- data/lib/mini_profiler/storage/redis_store.rb +0 -44
@@ -1,100 +1,131 @@
|
|
1
|
-
module Rack
|
2
|
-
class MiniProfiler
|
3
|
-
module ProfilingMethods
|
4
|
-
|
5
|
-
|
6
|
-
c = current
|
7
|
-
return unless c
|
8
|
-
|
9
|
-
|
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
|
-
|
27
|
-
# perform a profiling step on given block
|
28
|
-
def step(name, opts = nil)
|
29
|
-
if current
|
30
|
-
parent_timer = current.current_timer
|
31
|
-
result = nil
|
32
|
-
current.current_timer = current_timer = current.current_timer.add_child(name)
|
33
|
-
begin
|
34
|
-
result = yield if block_given?
|
35
|
-
ensure
|
36
|
-
current_timer.record_time
|
37
|
-
current.current_timer = parent_timer
|
38
|
-
end
|
39
|
-
else
|
40
|
-
yield if block_given?
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def unprofile_method(klass, method)
|
45
|
-
|
46
|
-
clean = clean_method_name(method)
|
47
|
-
|
48
|
-
with_profiling = ("#{clean}_with_mini_profiler").intern
|
49
|
-
without_profiling = ("#{clean}_without_mini_profiler").intern
|
50
|
-
|
51
|
-
if klass.send :method_defined?, with_profiling
|
52
|
-
klass.send :alias_method, method, without_profiling
|
53
|
-
klass.send :remove_method, with_profiling
|
54
|
-
klass.send :remove_method, without_profiling
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def profile_method(klass, method, &blk)
|
59
|
-
default_name = klass.to_s + " " + method.to_s
|
60
|
-
clean = clean_method_name(method)
|
61
|
-
|
62
|
-
with_profiling = ("#{clean}_with_mini_profiler").intern
|
63
|
-
without_profiling = ("#{clean}_without_mini_profiler").intern
|
64
|
-
|
65
|
-
if klass.send :method_defined?, with_profiling
|
66
|
-
return # dont double profile
|
67
|
-
end
|
68
|
-
|
69
|
-
klass.send :alias_method, without_profiling, method
|
70
|
-
klass.send :define_method, with_profiling do |*args, &orig|
|
71
|
-
return self.send without_profiling, *args, &orig unless Rack::MiniProfiler.current
|
72
|
-
|
73
|
-
name = default_name
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
module ProfilingMethods
|
4
|
+
|
5
|
+
def record_sql(query, elapsed_ms)
|
6
|
+
c = current
|
7
|
+
return unless c
|
8
|
+
c.current_timer.add_sql(query, elapsed_ms, c.page_struct, c.skip_backtrace, c.full_backtrace) if (c && c.current_timer)
|
9
|
+
end
|
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
|
+
|
27
|
+
# perform a profiling step on given block
|
28
|
+
def step(name, opts = nil)
|
29
|
+
if current
|
30
|
+
parent_timer = current.current_timer
|
31
|
+
result = nil
|
32
|
+
current.current_timer = current_timer = current.current_timer.add_child(name)
|
33
|
+
begin
|
34
|
+
result = yield if block_given?
|
35
|
+
ensure
|
36
|
+
current_timer.record_time
|
37
|
+
current.current_timer = parent_timer
|
38
|
+
end
|
39
|
+
else
|
40
|
+
yield if block_given?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def unprofile_method(klass, method)
|
45
|
+
|
46
|
+
clean = clean_method_name(method)
|
47
|
+
|
48
|
+
with_profiling = ("#{clean}_with_mini_profiler").intern
|
49
|
+
without_profiling = ("#{clean}_without_mini_profiler").intern
|
50
|
+
|
51
|
+
if klass.send :method_defined?, with_profiling
|
52
|
+
klass.send :alias_method, method, without_profiling
|
53
|
+
klass.send :remove_method, with_profiling
|
54
|
+
klass.send :remove_method, without_profiling
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def profile_method(klass, method, &blk)
|
59
|
+
default_name = klass.to_s + " " + method.to_s
|
60
|
+
clean = clean_method_name(method)
|
61
|
+
|
62
|
+
with_profiling = ("#{clean}_with_mini_profiler").intern
|
63
|
+
without_profiling = ("#{clean}_without_mini_profiler").intern
|
64
|
+
|
65
|
+
if klass.send :method_defined?, with_profiling
|
66
|
+
return # dont double profile
|
67
|
+
end
|
68
|
+
|
69
|
+
klass.send :alias_method, without_profiling, method
|
70
|
+
klass.send :define_method, with_profiling do |*args, &orig|
|
71
|
+
return self.send without_profiling, *args, &orig unless Rack::MiniProfiler.current
|
72
|
+
|
73
|
+
name = default_name
|
74
|
+
if blk
|
75
|
+
name =
|
76
|
+
if respond_to?(:instance_exec)
|
77
|
+
instance_exec(*args, &blk)
|
78
|
+
else
|
79
|
+
# deprecated in Rails 4.x
|
80
|
+
blk.bind(self).call(*args)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
parent_timer = Rack::MiniProfiler.current.current_timer
|
85
|
+
page_struct = Rack::MiniProfiler.current.page_struct
|
86
|
+
result = nil
|
87
|
+
|
88
|
+
Rack::MiniProfiler.current.current_timer = current_timer = parent_timer.add_child(name)
|
89
|
+
begin
|
90
|
+
result = self.send without_profiling, *args, &orig
|
91
|
+
ensure
|
92
|
+
current_timer.record_time
|
93
|
+
Rack::MiniProfiler.current.current_timer = parent_timer
|
94
|
+
end
|
95
|
+
result
|
96
|
+
end
|
97
|
+
klass.send :alias_method, method, with_profiling
|
98
|
+
end
|
99
|
+
|
100
|
+
# Add a custom timing. These are displayed similar to SQL/query time in
|
101
|
+
# columns expanding to the right.
|
102
|
+
#
|
103
|
+
# type - String counter type. Each distinct type gets its own column.
|
104
|
+
# duration_ms - Duration of the call in ms. Either this or a block must be
|
105
|
+
# given but not both.
|
106
|
+
#
|
107
|
+
# When a block is given, calculate the duration by yielding to the block
|
108
|
+
# and keeping a record of its run time.
|
109
|
+
#
|
110
|
+
# Returns the result of the block, or nil when no block is given.
|
111
|
+
def counter(type, duration_ms=nil)
|
112
|
+
result = nil
|
113
|
+
if block_given?
|
114
|
+
start = Time.now
|
115
|
+
result = yield
|
116
|
+
duration_ms = (Time.now - start).to_f * 1000
|
117
|
+
end
|
118
|
+
return result if current.nil? || !request_authorized?
|
119
|
+
current.current_timer.add_custom(type, duration_ms, current.page_struct)
|
120
|
+
result
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def clean_method_name(method)
|
126
|
+
method.to_s.gsub(/[\?\!]/, "")
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -33,7 +33,9 @@ module Rack
|
|
33
33
|
"Depth"=> parent ? parent.depth + 1 : 0,
|
34
34
|
"ExecutedReaders"=> 0,
|
35
35
|
"ExecutedScalars"=> 0,
|
36
|
-
"ExecutedNonQueries"=> 0
|
36
|
+
"ExecutedNonQueries"=> 0,
|
37
|
+
"CustomTimingStats" => {},
|
38
|
+
"CustomTimings" => {})
|
37
39
|
@children_duration = 0
|
38
40
|
@start = Time.now
|
39
41
|
@parent = parent
|
@@ -79,6 +81,23 @@ module Rack
|
|
79
81
|
timer
|
80
82
|
end
|
81
83
|
|
84
|
+
def add_custom(type, elapsed_ms, page)
|
85
|
+
timer = CustomTimerStruct.new(type, elapsed_ms, page, self)
|
86
|
+
timer['ParentTimingId'] = self['Id']
|
87
|
+
self['CustomTimings'][type] ||= []
|
88
|
+
self['CustomTimings'][type].push(timer)
|
89
|
+
|
90
|
+
self['CustomTimingStats'][type] ||= {"Count" => 0, "Duration" => 0.0}
|
91
|
+
self['CustomTimingStats'][type]['Count'] += 1
|
92
|
+
self['CustomTimingStats'][type]['Duration'] += elapsed_ms
|
93
|
+
|
94
|
+
page['CustomTimingStats'][type] ||= {"Count" => 0, "Duration" => 0.0}
|
95
|
+
page['CustomTimingStats'][type]['Count'] += 1
|
96
|
+
page['CustomTimingStats'][type]['Duration'] += elapsed_ms
|
97
|
+
|
98
|
+
timer
|
99
|
+
end
|
100
|
+
|
82
101
|
def record_time(milliseconds = nil)
|
83
102
|
milliseconds ||= (Time.now - @start) * 1000
|
84
103
|
self['DurationMilliseconds'] = milliseconds
|
File without changes
|
@@ -1,27 +1,31 @@
|
|
1
|
-
module Rack
|
2
|
-
class MiniProfiler
|
3
|
-
class AbstractStore
|
4
|
-
|
5
|
-
def save(page_struct)
|
6
|
-
raise NotImplementedError.new("save is not implemented")
|
7
|
-
end
|
8
|
-
|
9
|
-
def load(id)
|
10
|
-
raise NotImplementedError.new("load is not implemented")
|
11
|
-
end
|
12
|
-
|
13
|
-
def set_unviewed(user, id)
|
14
|
-
raise NotImplementedError.new("set_unviewed is not implemented")
|
15
|
-
end
|
16
|
-
|
17
|
-
def set_viewed(user, id)
|
18
|
-
raise NotImplementedError.new("set_viewed is not implemented")
|
19
|
-
end
|
20
|
-
|
21
|
-
def get_unviewed_ids(user)
|
22
|
-
raise NotImplementedError.new("get_unviewed_ids is not implemented")
|
23
|
-
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
class AbstractStore
|
4
|
+
|
5
|
+
def save(page_struct)
|
6
|
+
raise NotImplementedError.new("save is not implemented")
|
7
|
+
end
|
8
|
+
|
9
|
+
def load(id)
|
10
|
+
raise NotImplementedError.new("load is not implemented")
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_unviewed(user, id)
|
14
|
+
raise NotImplementedError.new("set_unviewed is not implemented")
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_viewed(user, id)
|
18
|
+
raise NotImplementedError.new("set_viewed is not implemented")
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_unviewed_ids(user)
|
22
|
+
raise NotImplementedError.new("get_unviewed_ids is not implemented")
|
23
|
+
end
|
24
|
+
|
25
|
+
def diagnostics(user)
|
26
|
+
raise NotImplementedError.new("diagnostics not implemented")
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,109 +1,111 @@
|
|
1
|
-
module Rack
|
2
|
-
class MiniProfiler
|
3
|
-
class FileStore < AbstractStore
|
4
|
-
|
5
|
-
class FileCache
|
6
|
-
def initialize(path, prefix)
|
7
|
-
@path = path
|
8
|
-
@prefix = prefix
|
9
|
-
end
|
10
|
-
|
11
|
-
def [](key)
|
12
|
-
begin
|
13
|
-
data = ::File.open(path(key),"rb") {|f| f.read}
|
14
|
-
return Marshal.load data
|
15
|
-
rescue => e
|
16
|
-
return nil
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def []=(key,val)
|
21
|
-
::File.open(path(key), "wb+") {|f| f.write Marshal.dump(val)}
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
def path(key)
|
26
|
-
@path + "/" + @prefix + "_" + key
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
def initialize(args)
|
33
|
-
|
34
|
-
|
35
|
-
@
|
36
|
-
|
37
|
-
@
|
38
|
-
@
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
current
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
current
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
end
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
class FileStore < AbstractStore
|
4
|
+
|
5
|
+
class FileCache
|
6
|
+
def initialize(path, prefix)
|
7
|
+
@path = path
|
8
|
+
@prefix = prefix
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
begin
|
13
|
+
data = ::File.open(path(key),"rb") {|f| f.read}
|
14
|
+
return Marshal.load data
|
15
|
+
rescue => e
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def []=(key,val)
|
21
|
+
::File.open(path(key), "wb+") {|f| f.write Marshal.dump(val)}
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def path(key)
|
26
|
+
@path + "/" + @prefix + "_" + key
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
EXPIRES_IN_SECONDS = 60 * 60 * 24
|
31
|
+
|
32
|
+
def initialize(args = nil)
|
33
|
+
args ||= {}
|
34
|
+
@path = args[:path]
|
35
|
+
@expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
|
36
|
+
raise ArgumentError.new :path unless @path
|
37
|
+
@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
|
41
|
+
|
42
|
+
me = self
|
43
|
+
Thread.new do
|
44
|
+
begin
|
45
|
+
while true do
|
46
|
+
# TODO: a sane retry count before bailing
|
47
|
+
me.cleanup_cache
|
48
|
+
sleep(3600)
|
49
|
+
end
|
50
|
+
rescue
|
51
|
+
# don't crash the thread, we can clean up next time
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def save(page_struct)
|
57
|
+
@timer_struct_lock.synchronize {
|
58
|
+
@timer_struct_cache[page_struct['Id']] = page_struct
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def load(id)
|
63
|
+
@timer_struct_lock.synchronize {
|
64
|
+
@timer_struct_cache[id]
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def set_unviewed(user, id)
|
69
|
+
@user_view_lock.synchronize {
|
70
|
+
current = @user_view_cache[user]
|
71
|
+
current = [] unless Array === current
|
72
|
+
current << id
|
73
|
+
@user_view_cache[user] = current.uniq
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def set_viewed(user, id)
|
78
|
+
@user_view_lock.synchronize {
|
79
|
+
@user_view_cache[user] ||= []
|
80
|
+
current = @user_view_cache[user]
|
81
|
+
current = [] unless Array === current
|
82
|
+
current.delete(id)
|
83
|
+
@user_view_cache[user] = current.uniq
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_unviewed_ids(user)
|
88
|
+
@user_view_lock.synchronize {
|
89
|
+
@user_view_cache[user]
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def cleanup_cache
|
94
|
+
files = Dir.entries(@path)
|
95
|
+
@timer_struct_lock.synchronize {
|
96
|
+
files.each do |f|
|
97
|
+
f = @path + '/' + f
|
98
|
+
::File.delete f if ::File.basename(f) =~ /^mp_timers/ and (Time.now - ::File.mtime(f)) > @expires_in_seconds
|
99
|
+
end
|
100
|
+
}
|
101
|
+
@user_view_lock.synchronize {
|
102
|
+
files.each do |f|
|
103
|
+
f = @path + '/' + f
|
104
|
+
::File.delete f if ::File.basename(f) =~ /^mp_views/ and (Time.now - ::File.mtime(f)) > @expires_in_seconds
|
105
|
+
end
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|