rack-mini-profiler 0.10.6 → 2.3.0
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +129 -16
- data/README.md +116 -63
- data/lib/enable_rails_patches.rb +5 -0
- data/lib/generators/rack_profiler/install_generator.rb +2 -0
- data/lib/generators/rack_profiler/templates/rack_profiler.rb +2 -0
- data/lib/html/dot.1.1.2.min.js +2 -0
- data/lib/html/includes.css +141 -40
- data/lib/html/includes.js +1398 -970
- data/lib/html/includes.scss +547 -442
- data/lib/html/includes.tmpl +227 -142
- data/lib/html/pretty-print.js +810 -0
- data/lib/html/profile_handler.js +1 -1
- data/lib/html/rack-mini-profiler.css +3 -0
- data/lib/html/rack-mini-profiler.js +2 -0
- data/lib/html/share.html +0 -1
- data/lib/html/speedscope/LICENSE +21 -0
- data/lib/html/speedscope/README.md +3 -0
- data/lib/html/speedscope/demangle-cpp.1768f4cc.js +4 -0
- data/lib/html/speedscope/favicon-16x16.f74b3187.png +0 -0
- data/lib/html/speedscope/favicon-32x32.bc503437.png +0 -0
- data/lib/html/speedscope/file-format-schema.json +324 -0
- data/lib/html/speedscope/import.cf0fa83f.js +115 -0
- data/lib/html/speedscope/index.html +2 -0
- data/lib/html/speedscope/release.txt +3 -0
- data/lib/html/speedscope/reset.8c46b7a1.css +2 -0
- data/lib/html/speedscope/source-map.438fa06b.js +24 -0
- data/lib/html/speedscope/speedscope.44364064.js +200 -0
- data/lib/html/vendor.js +848 -0
- data/lib/mini_profiler/asset_version.rb +3 -2
- data/lib/mini_profiler/client_settings.rb +27 -16
- data/lib/mini_profiler/config.rb +73 -46
- data/lib/mini_profiler/context.rb +5 -3
- data/lib/mini_profiler/gc_profiler.rb +17 -16
- data/lib/mini_profiler/profiler.rb +332 -94
- data/lib/mini_profiler/profiling_methods.rb +20 -15
- data/lib/mini_profiler/snapshots_transporter.rb +109 -0
- data/lib/mini_profiler/storage/abstract_store.rb +80 -0
- data/lib/mini_profiler/storage/file_store.rb +18 -13
- data/lib/mini_profiler/storage/memcache_store.rb +10 -7
- data/lib/mini_profiler/storage/memory_store.rb +63 -13
- data/lib/mini_profiler/storage/redis_store.rb +143 -7
- data/lib/mini_profiler/timer_struct/base.rb +4 -2
- data/lib/mini_profiler/timer_struct/client.rb +9 -8
- data/lib/mini_profiler/timer_struct/custom.rb +8 -5
- data/lib/mini_profiler/timer_struct/page.rb +79 -24
- data/lib/mini_profiler/timer_struct/request.rb +83 -38
- data/lib/mini_profiler/timer_struct/sql.rb +25 -22
- data/lib/mini_profiler/version.rb +3 -1
- data/lib/mini_profiler_rails/railtie.rb +91 -8
- data/lib/mini_profiler_rails/railtie_methods.rb +61 -0
- data/lib/patches/db/activerecord.rb +5 -14
- data/lib/patches/db/mongo.rb +3 -1
- data/lib/patches/db/moped.rb +5 -3
- data/lib/patches/db/mysql2.rb +8 -6
- data/lib/patches/db/neo4j.rb +3 -1
- data/lib/patches/db/nobrainer.rb +4 -2
- data/lib/patches/db/oracle_enhanced.rb +4 -2
- data/lib/patches/db/pg.rb +41 -21
- data/lib/patches/db/plucky.rb +7 -5
- data/lib/patches/db/riak.rb +15 -13
- data/lib/patches/db/rsolr.rb +6 -4
- data/lib/patches/db/sequel.rb +2 -0
- data/lib/patches/net_patches.rb +20 -8
- data/lib/patches/sql_patches.rb +17 -7
- data/lib/prepend_net_http_patch.rb +5 -0
- data/lib/rack-mini-profiler.rb +3 -3
- data/rack-mini-profiler.gemspec +23 -9
- metadata +146 -31
- data/lib/html/jquery.1.7.1.js +0 -4
- data/lib/html/jquery.tmpl.js +0 -486
- data/lib/html/list.css +0 -9
- data/lib/html/list.js +0 -38
- data/lib/html/list.tmpl +0 -34
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
1
5
|
module Rack
|
2
6
|
class MiniProfiler
|
3
7
|
class RedisStore < AbstractStore
|
@@ -8,7 +12,7 @@ module Rack
|
|
8
12
|
|
9
13
|
def initialize(args = nil)
|
10
14
|
@args = args || {}
|
11
|
-
@prefix = @args.delete(:prefix)
|
15
|
+
@prefix = @args.delete(:prefix) || 'MPRedisStore'
|
12
16
|
@redis_connection = @args.delete(:connection)
|
13
17
|
@expires_in_seconds = @args.delete(:expires_in) || EXPIRES_IN_SECONDS
|
14
18
|
end
|
@@ -31,8 +35,8 @@ module Rack
|
|
31
35
|
|
32
36
|
def set_unviewed(user, id)
|
33
37
|
key = user_key(user)
|
34
|
-
if redis.exists
|
35
|
-
expire_at =
|
38
|
+
if redis.call([:exists, prefixed_id(id)]) == 1
|
39
|
+
expire_at = Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i + redis.ttl(prefixed_id(id))
|
36
40
|
redis.zadd(key, expire_at, id)
|
37
41
|
end
|
38
42
|
redis.expire(key, @expires_in_seconds)
|
@@ -42,8 +46,8 @@ module Rack
|
|
42
46
|
key = user_key(user)
|
43
47
|
redis.del(key)
|
44
48
|
ids.each do |id|
|
45
|
-
if redis.exists
|
46
|
-
expire_at =
|
49
|
+
if redis.call([:exists, prefixed_id(id)]) == 1
|
50
|
+
expire_at = Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i + redis.ttl(prefixed_id(id))
|
47
51
|
redis.zadd(key, expire_at, id)
|
48
52
|
end
|
49
53
|
end
|
@@ -57,7 +61,7 @@ module Rack
|
|
57
61
|
# Remove expired ids from the unviewed sorted set and return the remaining ids
|
58
62
|
def get_unviewed_ids(user)
|
59
63
|
key = user_key(user)
|
60
|
-
redis.zremrangebyscore(key, '-inf',
|
64
|
+
redis.zremrangebyscore(key, '-inf', Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i)
|
61
65
|
redis.zrevrangebyscore(key, '+inf', '-inf')
|
62
66
|
end
|
63
67
|
|
@@ -101,11 +105,111 @@ unviewed_ids: #{get_unviewed_ids(user)}
|
|
101
105
|
end
|
102
106
|
|
103
107
|
redis.setex "#{@prefix}-key1", timeout, key1
|
104
|
-
redis.setex "#{@prefix}-key1_old", timeout*2, key1
|
108
|
+
redis.setex "#{@prefix}-key1_old", timeout * 2, key1
|
105
109
|
|
106
110
|
[key1, key2].compact
|
107
111
|
end
|
108
112
|
|
113
|
+
COUNTER_LUA = <<~LUA
|
114
|
+
if redis.call("INCR", KEYS[1]) % ARGV[1] == 0 then
|
115
|
+
redis.call("DEL", KEYS[1])
|
116
|
+
return 1
|
117
|
+
else
|
118
|
+
return 0
|
119
|
+
end
|
120
|
+
LUA
|
121
|
+
|
122
|
+
COUNTER_LUA_SHA = Digest::SHA1.hexdigest(COUNTER_LUA)
|
123
|
+
|
124
|
+
def should_take_snapshot?(period)
|
125
|
+
1 == cached_redis_eval(
|
126
|
+
COUNTER_LUA,
|
127
|
+
COUNTER_LUA_SHA,
|
128
|
+
reraise: false,
|
129
|
+
keys: [snapshot_counter_key()],
|
130
|
+
argv: [period]
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
def push_snapshot(page_struct, config)
|
135
|
+
zset_key = snapshot_zset_key()
|
136
|
+
hash_key = snapshot_hash_key()
|
137
|
+
|
138
|
+
id = page_struct[:id]
|
139
|
+
score = page_struct.duration_ms
|
140
|
+
limit = config.snapshots_limit
|
141
|
+
bytes = Marshal.dump(page_struct)
|
142
|
+
|
143
|
+
lua = <<~LUA
|
144
|
+
local zset_key = KEYS[1]
|
145
|
+
local hash_key = KEYS[2]
|
146
|
+
local id = ARGV[1]
|
147
|
+
local score = tonumber(ARGV[2])
|
148
|
+
local bytes = ARGV[3]
|
149
|
+
local limit = tonumber(ARGV[4])
|
150
|
+
redis.call("ZADD", zset_key, score, id)
|
151
|
+
redis.call("HSET", hash_key, id, bytes)
|
152
|
+
if redis.call("ZCARD", zset_key) > limit then
|
153
|
+
local lowest_snapshot_id = redis.call("ZRANGE", zset_key, 0, 0)[1]
|
154
|
+
redis.call("ZREM", zset_key, lowest_snapshot_id)
|
155
|
+
redis.call("HDEL", hash_key, lowest_snapshot_id)
|
156
|
+
end
|
157
|
+
LUA
|
158
|
+
redis.eval(
|
159
|
+
lua,
|
160
|
+
keys: [zset_key, hash_key],
|
161
|
+
argv: [id, score, bytes, limit]
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
def fetch_snapshots(batch_size: 200, &blk)
|
166
|
+
zset_key = snapshot_zset_key()
|
167
|
+
hash_key = snapshot_hash_key()
|
168
|
+
iteration = 0
|
169
|
+
corrupt_snapshots = []
|
170
|
+
while true
|
171
|
+
ids = redis.zrange(
|
172
|
+
zset_key,
|
173
|
+
batch_size * iteration,
|
174
|
+
batch_size * iteration + batch_size - 1
|
175
|
+
)
|
176
|
+
break if ids.size == 0
|
177
|
+
batch = redis.mapped_hmget(hash_key, *ids).to_a
|
178
|
+
batch.map! do |id, bytes|
|
179
|
+
begin
|
180
|
+
Marshal.load(bytes)
|
181
|
+
rescue
|
182
|
+
corrupt_snapshots << id
|
183
|
+
nil
|
184
|
+
end
|
185
|
+
end
|
186
|
+
batch.compact!
|
187
|
+
blk.call(batch) if batch.size != 0
|
188
|
+
break if ids.size < batch_size
|
189
|
+
iteration += 1
|
190
|
+
end
|
191
|
+
if corrupt_snapshots.size > 0
|
192
|
+
redis.pipelined do
|
193
|
+
redis.zrem(zset_key, corrupt_snapshots)
|
194
|
+
redis.hdel(hash_key, corrupt_snapshots)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def load_snapshot(id)
|
200
|
+
hash_key = snapshot_hash_key()
|
201
|
+
bytes = redis.hget(hash_key, id)
|
202
|
+
begin
|
203
|
+
Marshal.load(bytes)
|
204
|
+
rescue
|
205
|
+
redis.pipelined do
|
206
|
+
redis.zrem(snapshot_zset_key(), id)
|
207
|
+
redis.hdel(hash_key, id)
|
208
|
+
end
|
209
|
+
nil
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
109
213
|
private
|
110
214
|
|
111
215
|
def user_key(user)
|
@@ -123,6 +227,38 @@ unviewed_ids: #{get_unviewed_ids(user)}
|
|
123
227
|
end
|
124
228
|
end
|
125
229
|
|
230
|
+
def snapshot_counter_key
|
231
|
+
@snapshot_counter_key ||= "#{@prefix}-mini-profiler-snapshots-counter"
|
232
|
+
end
|
233
|
+
|
234
|
+
def snapshot_zset_key
|
235
|
+
@snapshot_zset_key ||= "#{@prefix}-mini-profiler-snapshots-zset"
|
236
|
+
end
|
237
|
+
|
238
|
+
def snapshot_hash_key
|
239
|
+
@snapshot_hash_key ||= "#{@prefix}-mini-profiler-snapshots-hash"
|
240
|
+
end
|
241
|
+
|
242
|
+
def cached_redis_eval(script, script_sha, reraise: true, argv: [], keys: [])
|
243
|
+
begin
|
244
|
+
redis.evalsha(script_sha, argv: argv, keys: keys)
|
245
|
+
rescue ::Redis::CommandError => e
|
246
|
+
if e.message.start_with?('NOSCRIPT')
|
247
|
+
redis.eval(script, argv: argv, keys: keys)
|
248
|
+
else
|
249
|
+
raise e if reraise
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# only used in tests
|
255
|
+
def wipe_snapshots_data
|
256
|
+
redis.pipelined do
|
257
|
+
redis.del(snapshot_counter_key())
|
258
|
+
redis.del(snapshot_zset_key())
|
259
|
+
redis.del(snapshot_hash_key())
|
260
|
+
end
|
261
|
+
end
|
126
262
|
end
|
127
263
|
end
|
128
264
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
class MiniProfiler
|
3
5
|
module TimerStruct
|
4
6
|
# A base class for timing structures
|
5
7
|
class Base
|
6
8
|
|
7
|
-
def initialize(attrs={})
|
9
|
+
def initialize(attrs = {})
|
8
10
|
@attributes = attrs
|
9
11
|
end
|
10
12
|
|
@@ -24,7 +26,7 @@ module Rack
|
|
24
26
|
def to_json(*a)
|
25
27
|
# this does could take in an option hash, but the only interesting there is max_nesting.
|
26
28
|
# if this becomes an option we could increase
|
27
|
-
::JSON.generate(
|
29
|
+
::JSON.generate(@attributes, max_nesting: 100)
|
28
30
|
end
|
29
31
|
|
30
32
|
def as_json(options = nil)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
class MiniProfiler
|
3
5
|
module TimerStruct
|
@@ -15,15 +17,14 @@ module Rack
|
|
15
17
|
|
16
18
|
# used by Railtie to instrument asset_tag for JS / CSS
|
17
19
|
def self.instrument(name, orig)
|
18
|
-
probe = "<script>mPt.probe('#{name}')</script>"
|
20
|
+
probe = "<script>mPt.probe('#{name}')</script>".dup
|
19
21
|
wrapped = probe
|
20
22
|
wrapped << orig
|
21
23
|
wrapped << probe
|
22
24
|
wrapped
|
23
25
|
end
|
24
26
|
|
25
|
-
|
26
|
-
def initialize(env={})
|
27
|
+
def initialize(env = {})
|
27
28
|
super
|
28
29
|
end
|
29
30
|
|
@@ -60,21 +61,21 @@ module Rack
|
|
60
61
|
end
|
61
62
|
|
62
63
|
translated.each do |name, data|
|
63
|
-
h = {"Name" => name, "Start" => data[:start].to_i - baseTime}
|
64
|
+
h = { "Name" => name, "Start" => data[:start].to_i - baseTime }
|
64
65
|
h["Duration"] = data[:finish].to_i - data[:start].to_i if data[:finish]
|
65
66
|
timings.push(h)
|
66
67
|
end
|
67
68
|
|
68
|
-
clientTimes.keys.find_all{|k| k =~ /Start$/ }.each do |k|
|
69
|
+
clientTimes.keys.find_all { |k| k =~ /Start$/ }.each do |k|
|
69
70
|
start = clientTimes[k].to_i - baseTime
|
70
71
|
finish = clientTimes[k.sub(/Start$/, "End")].to_i - baseTime
|
71
72
|
duration = 0
|
72
73
|
duration = finish - start if finish > start
|
73
|
-
name = k.sub(/Start$/, "").split(/(?=[A-Z])/).map{|s| s.capitalize}.join(' ')
|
74
|
-
timings.push(
|
74
|
+
name = k.sub(/Start$/, "").split(/(?=[A-Z])/).map { |s| s.capitalize }.join(' ')
|
75
|
+
timings.push("Name" => name, "Start" => start, "Duration" => duration) if start >= 0
|
75
76
|
end
|
76
77
|
|
77
|
-
clientTimes.keys.find_all{|k| !(k =~ /(End|Start)$/)}.each do |k|
|
78
|
+
clientTimes.keys.find_all { |k| !(k =~ /(End|Start)$/) }.each do |k|
|
78
79
|
timings.push("Name" => k, "Start" => clientTimes[k].to_i - baseTime, "Duration" => -1)
|
79
80
|
end
|
80
81
|
|
@@ -1,19 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
class MiniProfiler
|
3
5
|
module TimerStruct
|
4
6
|
# Timing system for a custom timers such as cache, redis, RPC, external API
|
5
7
|
# calls, etc.
|
6
8
|
class Custom < TimerStruct::Base
|
9
|
+
attr_accessor :parent
|
7
10
|
def initialize(type, duration_ms, page, parent)
|
8
11
|
@parent = parent
|
9
12
|
@page = page
|
10
13
|
@type = type
|
11
|
-
start_millis = ((
|
14
|
+
start_millis = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i - page[:started]) - duration_ms
|
12
15
|
super(
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
16
|
+
type: type,
|
17
|
+
start_milliseconds: start_millis,
|
18
|
+
duration_milliseconds: duration_ms,
|
19
|
+
parent_timing_id: nil
|
17
20
|
)
|
18
21
|
end
|
19
22
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
class MiniProfiler
|
3
5
|
module TimerStruct
|
@@ -8,34 +10,87 @@ module Rack
|
|
8
10
|
# :has_many TimerStruct::Sql children
|
9
11
|
# :has_many TimerStruct::Custom children
|
10
12
|
class Page < TimerStruct::Base
|
13
|
+
class << self
|
14
|
+
def from_hash(hash)
|
15
|
+
hash = symbolize_hash(hash)
|
16
|
+
if hash.key?(:custom_timing_names)
|
17
|
+
hash[:custom_timing_names] = []
|
18
|
+
end
|
19
|
+
hash.delete(:started_formatted)
|
20
|
+
if hash.key?(:duration_milliseconds)
|
21
|
+
hash[:duration_milliseconds] = 0
|
22
|
+
end
|
23
|
+
page = self.allocate
|
24
|
+
page.instance_variable_set(:@attributes, hash)
|
25
|
+
page
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def symbolize_hash(hash)
|
31
|
+
new_hash = {}
|
32
|
+
hash.each do |k, v|
|
33
|
+
sym_k = String === k ? k.to_sym : k
|
34
|
+
if Hash === v
|
35
|
+
new_hash[sym_k] = symbolize_hash(v)
|
36
|
+
elsif Array === v
|
37
|
+
new_hash[sym_k] = symbolize_array(v)
|
38
|
+
else
|
39
|
+
new_hash[sym_k] = v
|
40
|
+
end
|
41
|
+
end
|
42
|
+
new_hash
|
43
|
+
end
|
44
|
+
|
45
|
+
def symbolize_array(array)
|
46
|
+
array.map do |item|
|
47
|
+
if Array === item
|
48
|
+
symbolize_array(item)
|
49
|
+
elsif Hash === item
|
50
|
+
symbolize_hash(item)
|
51
|
+
else
|
52
|
+
item
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_reader :attributes
|
59
|
+
|
11
60
|
def initialize(env)
|
12
61
|
timer_id = MiniProfiler.generate_id
|
13
62
|
page_name = env['PATH_INFO']
|
14
63
|
started_at = (Time.now.to_f * 1000).to_i
|
64
|
+
started = (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i
|
15
65
|
machine_name = env['SERVER_NAME']
|
16
66
|
super(
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:started
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
67
|
+
id: timer_id,
|
68
|
+
name: page_name,
|
69
|
+
started: started,
|
70
|
+
started_at: started_at,
|
71
|
+
machine_name: machine_name,
|
72
|
+
level: 0,
|
73
|
+
user: "unknown user",
|
74
|
+
has_user_viewed: false,
|
75
|
+
client_timings: nil,
|
76
|
+
duration_milliseconds: 0,
|
77
|
+
has_trivial_timings: true,
|
78
|
+
has_all_trivial_timings: false,
|
79
|
+
trivial_duration_threshold_milliseconds: 2,
|
80
|
+
head: nil,
|
81
|
+
sql_count: 0,
|
82
|
+
duration_milliseconds_in_sql: 0,
|
83
|
+
has_sql_timings: true,
|
84
|
+
has_duplicate_sql_timings: false,
|
85
|
+
executed_readers: 0,
|
86
|
+
executed_scalars: 0,
|
87
|
+
executed_non_queries: 0,
|
88
|
+
custom_timing_names: [],
|
89
|
+
custom_timing_stats: {},
|
90
|
+
custom_fields: {}
|
38
91
|
)
|
92
|
+
self[:request_method] = env['REQUEST_METHOD']
|
93
|
+
self[:request_path] = env['PATH_INFO']
|
39
94
|
name = "#{env['REQUEST_METHOD']} http://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
|
40
95
|
self[:root] = TimerStruct::Request.createRoot(name, self)
|
41
96
|
end
|
@@ -66,9 +121,9 @@ module Rack
|
|
66
121
|
|
67
122
|
def extra_json
|
68
123
|
{
|
69
|
-
:
|
70
|
-
:
|
71
|
-
:
|
124
|
+
started_formatted: '/Date(%d)/' % @attributes[:started_at],
|
125
|
+
duration_milliseconds: @attributes[:root][:duration_milliseconds],
|
126
|
+
custom_timing_names: @attributes[:custom_timing_stats].keys.sort
|
72
127
|
}
|
73
128
|
end
|
74
129
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
class MiniProfiler
|
3
5
|
module TimerStruct
|
@@ -9,37 +11,37 @@ module Rack
|
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
12
|
-
attr_accessor :children_duration
|
14
|
+
attr_accessor :children_duration, :start, :parent
|
13
15
|
|
14
16
|
def initialize(name, page, parent)
|
15
|
-
start_millis = (
|
17
|
+
start_millis = (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i - page[:started]
|
16
18
|
depth = parent ? parent.depth + 1 : 0
|
17
19
|
super(
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
38
|
-
:
|
39
|
-
:
|
20
|
+
id: MiniProfiler.generate_id,
|
21
|
+
name: name,
|
22
|
+
duration_milliseconds: 0,
|
23
|
+
duration_without_children_milliseconds: 0,
|
24
|
+
start_milliseconds: start_millis,
|
25
|
+
parent_timing_id: nil,
|
26
|
+
children: [],
|
27
|
+
has_children: false,
|
28
|
+
key_values: nil,
|
29
|
+
has_sql_timings: false,
|
30
|
+
has_duplicate_sql_timings: false,
|
31
|
+
trivial_duration_threshold_milliseconds: 2,
|
32
|
+
sql_timings: [],
|
33
|
+
sql_timings_duration_milliseconds: 0,
|
34
|
+
is_trivial: false,
|
35
|
+
is_root: false,
|
36
|
+
depth: depth,
|
37
|
+
executed_readers: 0,
|
38
|
+
executed_scalars: 0,
|
39
|
+
executed_non_queries: 0,
|
40
|
+
custom_timing_stats: {},
|
41
|
+
custom_timings: {}
|
40
42
|
)
|
41
43
|
@children_duration = 0
|
42
|
-
@start =
|
44
|
+
@start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
43
45
|
@parent = parent
|
44
46
|
@page = page
|
45
47
|
end
|
@@ -60,10 +62,6 @@ module Rack
|
|
60
62
|
self[:start_milliseconds]
|
61
63
|
end
|
62
64
|
|
63
|
-
def start
|
64
|
-
@start
|
65
|
-
end
|
66
|
-
|
67
65
|
def depth
|
68
66
|
self[:depth]
|
69
67
|
end
|
@@ -89,6 +87,20 @@ module Rack
|
|
89
87
|
end
|
90
88
|
end
|
91
89
|
|
90
|
+
def move_child(child, destination)
|
91
|
+
if index = self[:children].index(child)
|
92
|
+
self[:children].slice!(index)
|
93
|
+
self[:has_children] = self[:children].size > 0
|
94
|
+
|
95
|
+
destination[:children].push(child)
|
96
|
+
destination[:has_children] = true
|
97
|
+
|
98
|
+
child[:parent_timing_id] = destination[:id]
|
99
|
+
child.parent = destination
|
100
|
+
child.adjust_depth
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
92
104
|
def add_sql(query, elapsed_ms, page, params = nil, skip_backtrace = false, full_backtrace = false)
|
93
105
|
TimerStruct::Sql.new(query, elapsed_ms, page, self, params, skip_backtrace, full_backtrace).tap do |timer|
|
94
106
|
self[:sql_timings].push(timer)
|
@@ -96,6 +108,20 @@ module Rack
|
|
96
108
|
self[:has_sql_timings] = true
|
97
109
|
self[:sql_timings_duration_milliseconds] += elapsed_ms
|
98
110
|
page[:duration_milliseconds_in_sql] += elapsed_ms
|
111
|
+
page[:sql_count] += 1
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def move_sql(sql, destination)
|
116
|
+
if index = self[:sql_timings].index(sql)
|
117
|
+
self[:sql_timings].slice!(index)
|
118
|
+
self[:has_sql_timings] = self[:sql_timings].size > 0
|
119
|
+
self[:sql_timings_duration_milliseconds] -= sql[:duration_milliseconds]
|
120
|
+
destination[:sql_timings].push(sql)
|
121
|
+
destination[:has_sql_timings] = true
|
122
|
+
destination[:sql_timings_duration_milliseconds] += sql[:duration_milliseconds]
|
123
|
+
sql[:parent_timing_id] = destination[:id]
|
124
|
+
sql.parent = destination
|
99
125
|
end
|
100
126
|
end
|
101
127
|
|
@@ -106,28 +132,47 @@ module Rack
|
|
106
132
|
self[:custom_timings][type] ||= []
|
107
133
|
self[:custom_timings][type].push(timer)
|
108
134
|
|
109
|
-
self[:custom_timing_stats][type] ||= {:
|
135
|
+
self[:custom_timing_stats][type] ||= { count: 0, duration: 0.0 }
|
110
136
|
self[:custom_timing_stats][type][:count] += 1
|
111
137
|
self[:custom_timing_stats][type][:duration] += elapsed_ms
|
112
138
|
|
113
|
-
page[:custom_timing_stats][type] ||= {:
|
139
|
+
page[:custom_timing_stats][type] ||= { count: 0, duration: 0.0 }
|
114
140
|
page[:custom_timing_stats][type][:count] += 1
|
115
141
|
page[:custom_timing_stats][type][:duration] += elapsed_ms
|
116
142
|
end
|
117
143
|
end
|
118
144
|
|
145
|
+
def move_custom(type, custom, destination)
|
146
|
+
if index = self[:custom_timings][type]&.index(custom)
|
147
|
+
custom[:parent_timing_id] = destination[:id]
|
148
|
+
custom.parent = destination
|
149
|
+
self[:custom_timings][type].slice!(index)
|
150
|
+
if self[:custom_timings][type].size == 0
|
151
|
+
self[:custom_timings].delete(type)
|
152
|
+
self[:custom_timing_stats].delete(type)
|
153
|
+
else
|
154
|
+
self[:custom_timing_stats][type][:count] -= 1
|
155
|
+
self[:custom_timing_stats][type][:duration] -= custom[:duration_milliseconds]
|
156
|
+
end
|
157
|
+
destination[:custom_timings][type] ||= []
|
158
|
+
destination[:custom_timings][type].push(custom)
|
159
|
+
destination[:custom_timing_stats][type] ||= { count: 0, duration: 0.0 }
|
160
|
+
destination[:custom_timing_stats][type][:count] += 1
|
161
|
+
destination[:custom_timing_stats][type][:duration] += custom[:duration_milliseconds]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
119
165
|
def record_time(milliseconds = nil)
|
120
|
-
milliseconds ||= (
|
166
|
+
milliseconds ||= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - @start) * 1000
|
121
167
|
self[:duration_milliseconds] = milliseconds
|
122
168
|
self[:is_trivial] = true if milliseconds < self[:trivial_duration_threshold_milliseconds]
|
123
|
-
self[:duration_without_children_milliseconds] = milliseconds -
|
124
|
-
|
125
|
-
if @parent
|
126
|
-
@parent.children_duration += milliseconds
|
127
|
-
end
|
128
|
-
|
169
|
+
self[:duration_without_children_milliseconds] = milliseconds - self[:children].sum(&:duration_ms)
|
129
170
|
end
|
130
171
|
|
172
|
+
def adjust_depth
|
173
|
+
self[:depth] = self.parent ? self.parent[:depth] + 1 : 0
|
174
|
+
self[:children].each(&:adjust_depth)
|
175
|
+
end
|
131
176
|
end
|
132
177
|
end
|
133
178
|
end
|