rack-mini-profiler 0.1.28 → 0.9.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.

Potentially problematic release.


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

Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/{Ruby/CHANGELOG → CHANGELOG} +27 -0
  3. data/{Ruby/README.md → README.md} +73 -31
  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 +3 -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 +11 -11
  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 +4 -4
  22. data/{Ruby/lib → lib}/mini_profiler/profiler.rb +165 -142
  23. data/{Ruby/lib → lib}/mini_profiler/profiling_methods.rb +31 -11
  24. data/{Ruby/lib → lib}/mini_profiler/request_timer_struct.rb +5 -5
  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 +6 -2
  34. data/{Ruby/lib → lib}/patches/net_patches.rb +0 -0
  35. data/{Ruby/lib → lib}/patches/sql_patches.rb +3 -2
  36. data/{Ruby/lib → lib}/rack-mini-profiler.rb +0 -0
  37. data/rack-mini-profiler.gemspec +14 -6
  38. metadata +153 -43
  39. data/Ruby/lib/html/flamegraph.html +0 -351
  40. data/Ruby/lib/html/profile_handler.js +0 -1
  41. data/Ruby/lib/mini_profiler/flame_graph.rb +0 -54
  42. data/Ruby/lib/mini_profiler/gc_profiler.rb +0 -107
  43. data/Ruby/lib/mini_profiler/version.rb +0 -5
@@ -1,351 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
5
- <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.0.8/d3.min.js"></script>
6
- <!-- <script src="http://cdnjs.cloudflare.com/ajax/libs/sugar/1.3.8/sugar.min.js"></script> -->
7
- <meta charset=utf-8 />
8
- <title>Flame Graph of Page</title>
9
- <style>
10
- .info {height: 40px;}
11
- .legend div {
12
- display: block;
13
- float: left;
14
- width: 150px;
15
- margin: 0 8px 8px;
16
- padding: 4px;
17
- height: 50px;
18
- }
19
- </style>
20
- </head>
21
- <body>
22
- <div class="graph"></div>
23
- <div class="info"></div>
24
- <div class="legend"></div>
25
-
26
- <script>
27
-
28
- var data = /*DATA*/;
29
- var maxX = 0;
30
- var maxY = 0;
31
-
32
- debounce = function(func, wait, trickle) {
33
- var timeout;
34
-
35
- timeout = null;
36
- return function() {
37
- var args, context, currentWait, later;
38
- context = this;
39
- args = arguments;
40
- later = function() {
41
- timeout = null;
42
- return func.apply(context, args);
43
- };
44
-
45
- if (timeout && trickle) {
46
- // already queued, let it through
47
- return;
48
- }
49
-
50
- if (typeof wait === "function") {
51
- currentWait = wait();
52
- } else {
53
- currentWait = wait;
54
- }
55
-
56
- if (timeout) {
57
- clearTimeout(timeout);
58
- }
59
-
60
- timeout = setTimeout(later, currentWait);
61
- return timeout;
62
- };
63
- };
64
-
65
-
66
- var guessGem = function(frame)
67
- {
68
- var split = frame.split('/gems/');
69
- if(split.length == 1) {
70
- split = frame.split('/app/');
71
- if(split.length == 1) {
72
- split = frame.split('/lib/');
73
- }
74
-
75
- split = split[Math.max(split.length-2,0)].split('/');
76
- return split[split.length-1];
77
- }
78
- else
79
- {
80
- return split[split.length -1].split('/')[0];
81
- }
82
- }
83
-
84
- var guessMethod = function(frame) {
85
- var split = frame.split('`');
86
- if(split.length == 2) {
87
- return split[1].split("'")[0];
88
- }
89
- return '?';
90
- }
91
-
92
- var guessFile = function(frame) {
93
- var split = frame.split(".rb:");
94
- if(split.length == 2) {
95
- split = split[0].split('/');
96
- return split[split.length - 1];
97
- }
98
- return "";
99
- }
100
-
101
- $.each(data, function(){
102
- maxX = Math.max(maxX, this.x + this.width);
103
- maxY = Math.max(maxY, this.y);
104
- this.shortName = /* guessGem(this.frame) + " " + guessFile(this.frame) + " " */ guessMethod(this.frame);
105
- });
106
-
107
- var width = $(window).width();
108
- var height = $(window).height() / 1.2;
109
-
110
- $('.graph').width(width).height(height);
111
-
112
- var xScale = d3.scale.linear()
113
- .domain([0, maxX])
114
- .range([0, width]);
115
-
116
- var yScale = d3.scale.linear()
117
- .domain([0, maxY])
118
- .range([0,height]);
119
-
120
- var realHeight = 0;
121
- var debouncedHeightCheck = debounce(function(){
122
- if (realHeight > 15) {
123
- svg.selectAll('text').attr('display','show');
124
- } else {
125
- svg.selectAll('text').attr('display','none');
126
- }
127
- }, 200);
128
-
129
- function zoom()
130
- {
131
- svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
132
-
133
- realHeight = yScale(1) * d3.event.scale;
134
- debouncedHeightCheck();
135
- }
136
-
137
- var svg = d3.select(".graph")
138
- .append("svg")
139
- .attr("width", "100%")
140
- .attr("height", "100%")
141
- .attr("pointer-events", "all")
142
- .append('svg:g')
143
- .call(d3.behavior.zoom().on("zoom", zoom))
144
- .append('svg:g');
145
-
146
-
147
- // so zoom works everywhere
148
- svg.append("rect")
149
- .attr("x",function(d) { return xScale(0); })
150
- .attr("y",function(d) { return yScale(0);})
151
- .attr("width", function(d){return xScale(maxX);})
152
- .attr("height", yScale(maxY))
153
- .attr("fill", "white");
154
-
155
- var color = function() {
156
- var r = parseInt(205 + Math.random() * 50);
157
- var g = parseInt(Math.random() * 230);
158
- var b = parseInt(Math.random() * 55);
159
- return "rgb(" + r + "," + g + "," + b + ")";
160
- }
161
- var info = {};
162
-
163
- // http://stackoverflow.com/questions/1960473/unique-values-in-an-array
164
- Array.prototype.getUnique = function() {
165
- var o = {}, a = []
166
- for (var i = 0; i < this.length; i++) o[this[i]] = 1
167
- for (var e in o) a.push(e)
168
- return a
169
- }
170
-
171
- var samplePercent = function(samples, exclusive){
172
- var info = " (" + samples +
173
- " sample" + (samples == 1 ? "" : "s") + " - " +
174
- ((samples / maxX) * 100).toFixed(2) + "%) ";
175
- if (exclusive) {
176
- info += " (" + exclusive +
177
- " exclusive - " +
178
- ((exclusive / maxX) * 100).toFixed(2) + "%) ";
179
- }
180
- return info;
181
- }
182
-
183
- var mouseover = function(d) {
184
- var i = info[d.frame];
185
- $('.info').text( d.frame + " " + samplePercent(i.samples.length, d.topFrame ? d.topFrame.exclusiveCount : 0));
186
- d3.selectAll(i.nodes)
187
- .attr('opacity',0.5);
188
- };
189
-
190
- var mouseout = function(d) {
191
- var i = info[d.frame];
192
- $('.info').text("");
193
- d3.selectAll(i.nodes)
194
- .attr('opacity',1);
195
- };
196
-
197
- // http://stackoverflow.com/a/7419630
198
- var rainbow = function(numOfSteps, step) {
199
- // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distiguishable vibrant markers in Google Maps and other apps.
200
- // Adam Cole, 2011-Sept-14
201
- // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
202
- var r, g, b;
203
- var h = step / numOfSteps;
204
- var i = ~~(h * 6);
205
- var f = h * 6 - i;
206
- var q = 1 - f;
207
- switch(i % 6){
208
- case 0: r = 1, g = f, b = 0; break;
209
- case 1: r = q, g = 1, b = 0; break;
210
- case 2: r = 0, g = 1, b = f; break;
211
- case 3: r = 0, g = q, b = 1; break;
212
- case 4: r = f, g = 0, b = 1; break;
213
- case 5: r = 1, g = 0, b = q; break;
214
- }
215
- var c = "#" + ("00" + (~ ~(r * 255)).toString(16)).slice(-2) + ("00" + (~ ~(g * 255)).toString(16)).slice(-2) + ("00" + (~ ~(b * 255)).toString(16)).slice(-2);
216
- return (c);
217
- }
218
-
219
- // assign some colors, analyze samples per gem
220
- var gemStats = {}
221
- var topFrames = {}
222
- var lastFrame = {frame: 'd52e04d-df28-41ed-a215-b6ec840a8ea5', x: -1}
223
-
224
- $.each(data, function(){
225
-
226
- var gem = guessGem(this.frame);
227
- var stat = gemStats[gem];
228
-
229
- if(!stat) {
230
- gemStats[gem] = stat = {samples: [], frames: []};
231
- }
232
-
233
- stat.frames.push(this.frame);
234
- for(var j=0; j < this.width; j++){
235
- stat.samples.push(this.x + j);
236
- }
237
- // This assumes the traversal is in order
238
- if (lastFrame.x != this.x) {
239
- var topFrame = topFrames[lastFrame.frame]
240
- if (!topFrame) {
241
- topFrames[lastFrame.frame] = topFrame = {exclusiveCount: 0}
242
- }
243
- topFrame.exclusiveCount += 1;
244
- lastFrame.topFrame = topFrame;
245
- }
246
- lastFrame = this;
247
-
248
- });
249
-
250
- var topFrame = topFrames[lastFrame.frame]
251
- if (!topFrame) {
252
- topFrames[lastFrame.frame] = topFrame = {exclusiveCount: 0}
253
- }
254
- topFrame.exclusiveCount += 1;
255
- lastFrame.topFrame = topFrame;
256
-
257
- var totalGems = 0;
258
- $.each(gemStats, function(){totalGems++;});
259
-
260
-
261
- var currentIndex = 0;
262
- $.each(gemStats, function(k,stat){
263
-
264
- stat.color = rainbow(totalGems, currentIndex);
265
- stat.samples = stat.samples.getUnique();
266
-
267
- for(var x=0; x < stat.frames.length; x++) {
268
- info[stat.frames[x]] = {nodes: [], samples: [], color: stat.color};
269
- }
270
-
271
- currentIndex += 1;
272
- });
273
-
274
-
275
- // see: http://bl.ocks.org/mundhradevang/1387786
276
- function fontSize(d,i) {
277
- var size = yScale(1) / 3;
278
- // var words = d.shortName.split(' ');
279
- var word = d.shortName; // words[0];
280
- var width = xScale(d.width+100);
281
- var height = yScale(1);
282
- var length = 0;
283
- d3.select(this).style("font-size", size + "px").text(word);
284
- while(((this.getBBox().width >= width) || (this.getBBox().height >= height)) && (size > 12))
285
- {
286
- size -= 0.1;
287
- d3.select(this).style("font-size", size + "px");
288
- }
289
-
290
- d3.select(this).attr("dy", size);
291
- }
292
-
293
- svg.selectAll("g")
294
- .data(data)
295
- .enter()
296
- .append("g")
297
- .each(function(){
298
- d3.select(this)
299
- .append("rect")
300
- .attr("x",function(d) { return xScale(d.x-1); })
301
- .attr("y",function(d) { return yScale(maxY - d.y);})
302
- .attr("width", function(d){return xScale(d.width);})
303
- .attr("height", yScale(1))
304
- .attr("fill", function(d){
305
- var i = info[d.frame];
306
- if(!i) {
307
- info[d.frame] = i = {nodes: [], samples: [], color: color()};
308
- }
309
- i.nodes.push(this);
310
- for(var j=0; j < d.width; j++){
311
- i.samples.push(d.x + j);
312
- }
313
- return i.color;
314
- })
315
- .on("mouseover", mouseover)
316
- .on("mouseout", mouseout);
317
-
318
- d3.select(this)
319
- .append("text")
320
- .attr("x",function(d) { return xScale(d.x - 0.98); })
321
- .attr("y",function(d) { return yScale(maxY - d.y);})
322
- .on("mouseover", mouseover)
323
- .on("mouseout", mouseout)
324
- .each(fontSize)
325
- .attr("display", "none");
326
-
327
- });
328
-
329
-
330
- // Samples may overlap on the same line
331
- for (var r in info) {
332
- if (info[r].samples) {
333
- info[r].samples = info[r].samples.getUnique();
334
- }
335
- };
336
-
337
-
338
- // render the legend
339
- $.each(gemStats, function(k,v){
340
- var node = $("<div></div>")
341
- .css("background-color", v.color)
342
- .text(k + " " + samplePercent(v.samples.length)) ;
343
- $('.legend').append(node);
344
- });
345
-
346
-
347
-
348
- </script>
349
- </body>
350
- </html>
351
-
@@ -1 +0,0 @@
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,54 +0,0 @@
1
- # inspired by https://github.com/brendangregg/FlameGraph
2
-
3
- class Rack::MiniProfiler::FlameGraph
4
- def initialize(stacks)
5
- @stacks = stacks
6
- end
7
-
8
- def graph_data
9
- height = 0
10
-
11
- table = []
12
- prev = []
13
-
14
- # a 2d array makes collapsing easy
15
- @stacks.each_with_index do |stack, pos|
16
- col = []
17
-
18
- stack.reverse.map{|r| r.to_s}.each_with_index do |frame, i|
19
-
20
- if !prev[i].nil?
21
- last_col = prev[i]
22
- if last_col[0] == frame
23
- last_col[1] += 1
24
- col << nil
25
- next
26
- end
27
- end
28
-
29
- prev[i] = [frame, 1]
30
- col << prev[i]
31
- end
32
- prev = prev[0..col.length-1].to_a
33
- table << col
34
- end
35
-
36
- data = []
37
-
38
- # a 1d array makes rendering easy
39
- table.each_with_index do |col, col_num|
40
- col.each_with_index do |row, row_num|
41
- next unless row && row.length == 2
42
- data << {
43
- :x => col_num + 1,
44
- :y => row_num + 1,
45
- :width => row[1],
46
- :frame => row[0]
47
- }
48
- end
49
- end
50
-
51
- data
52
- end
53
-
54
- end
@@ -1,107 +0,0 @@
1
- class Rack::MiniProfiler::GCProfiler
2
-
3
- def object_space_stats
4
- stats = {}
5
- ids = Set.new
6
- i=0
7
- ObjectSpace.each_object { |o|
8
- begin
9
- i = stats[o.class] || 0
10
- i += 1
11
- stats[o.class] = i
12
- ids << o.object_id if Integer === o.object_id
13
- rescue NoMethodError
14
- # Redis::Future undefines .class and .object_id super weird
15
- end
16
- }
17
- {:stats => stats, :ids => ids}
18
- end
19
-
20
- def diff_object_stats(before,after)
21
- diff = {}
22
- after.each do |k,v|
23
- diff[k] = v - (before[k] || 0)
24
- end
25
- before.each do |k,v|
26
- diff[k] = 0 - v unless after[k]
27
- end
28
-
29
- diff
30
- end
31
-
32
- def analyze_strings(ids_before,ids_after)
33
- result = {}
34
- ids_after.each do |id|
35
- obj = ObjectSpace._id2ref(id)
36
- if String === obj && !ids_before.include?(obj.object_id)
37
- result[obj] ||= 0
38
- result[obj] += 1
39
- end
40
- end
41
- result
42
- end
43
-
44
- def profile_gc_time(app,env)
45
- body = []
46
-
47
- begin
48
- GC::Profiler.clear
49
- GC::Profiler.enable
50
- b = app.call(env)[2]
51
- b.close if b.respond_to? :close
52
- body << "GC Profiler ran during this request, if it fired you will see the cost below:\n\n"
53
- body << GC::Profiler.result
54
- ensure
55
- GC.enable
56
- GC::Profiler.disable
57
- end
58
-
59
- return [200, {'Content-Type' => 'text/plain'}, body]
60
- end
61
-
62
- def profile_gc(app,env)
63
-
64
- body = [];
65
-
66
- stat_before,stat_after,diff,string_analysis = nil
67
- begin
68
- GC.disable
69
- stat_before = object_space_stats
70
- b = app.call(env)[2]
71
- b.close if b.respond_to? :close
72
- stat_after = object_space_stats
73
-
74
- diff = diff_object_stats(stat_before[:stats],stat_after[:stats])
75
- string_analysis = analyze_strings(stat_before[:ids], stat_after[:ids])
76
- ensure
77
- GC.enable
78
- end
79
-
80
-
81
- body << "
82
- ObjectSpace delta caused by request:
83
- --------------------------------------------\n"
84
- diff.to_a.reject{|k,v| v == 0}.sort{|x,y| y[1] <=> x[1]}.each do |k,v|
85
- body << "#{k} : #{v}\n" if v != 0
86
- end
87
-
88
- body << "\n
89
- ObjectSpace stats:
90
- -----------------\n"
91
-
92
- stat_after[:stats].to_a.sort{|x,y| y[1] <=> x[1]}.each do |k,v|
93
- body << "#{k} : #{v}\n"
94
- end
95
-
96
-
97
- body << "\n
98
- String stats:
99
- ------------\n"
100
-
101
- string_analysis.to_a.sort{|x,y| y[1] <=> x[1] }.take(1000).each do |string,count|
102
- body << "#{count} : #{string}\n"
103
- end
104
-
105
- return [200, {'Content-Type' => 'text/plain'}, body]
106
- end
107
- end