rack-mini-profiler 0.1.28 → 0.9.0
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.
- checksums.yaml +4 -4
- data/{Ruby/CHANGELOG → CHANGELOG} +27 -0
- data/{Ruby/README.md → README.md} +73 -31
- data/{Ruby/lib → lib}/html/includes.css +0 -0
- data/{Ruby/lib → lib}/html/includes.js +9 -8
- data/{Ruby/lib → lib}/html/includes.less +0 -0
- data/{Ruby/lib → lib}/html/includes.tmpl +3 -1
- data/{Ruby/lib → lib}/html/jquery.1.7.1.js +0 -0
- data/{Ruby/lib → lib}/html/jquery.tmpl.js +0 -0
- data/{Ruby/lib → lib}/html/list.css +2 -2
- data/{Ruby/lib → lib}/html/list.js +1 -1
- data/{Ruby/lib → lib}/html/list.tmpl +2 -2
- data/lib/html/profile_handler.js +1 -0
- data/{Ruby/lib → lib}/html/share.html +2 -2
- data/{Ruby/lib → lib}/mini_profiler/client_settings.rb +11 -11
- data/{Ruby/lib → lib}/mini_profiler/client_timer_struct.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/config.rb +11 -4
- data/{Ruby/lib → lib}/mini_profiler/context.rb +1 -1
- data/{Ruby/lib → lib}/mini_profiler/custom_timer_struct.rb +0 -0
- data/lib/mini_profiler/gc_profiler.rb +181 -0
- data/{Ruby/lib → lib}/mini_profiler/page_timer_struct.rb +4 -4
- data/{Ruby/lib → lib}/mini_profiler/profiler.rb +165 -142
- data/{Ruby/lib → lib}/mini_profiler/profiling_methods.rb +31 -11
- data/{Ruby/lib → lib}/mini_profiler/request_timer_struct.rb +5 -5
- data/{Ruby/lib → lib}/mini_profiler/sql_timer_struct.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/storage/abstract_store.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/storage/file_store.rb +26 -4
- data/{Ruby/lib → lib}/mini_profiler/storage/memcache_store.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/storage/memory_store.rb +25 -4
- data/{Ruby/lib → lib}/mini_profiler/storage/redis_store.rb +0 -0
- data/{Ruby/lib → lib}/mini_profiler/timer_struct.rb +0 -0
- data/lib/mini_profiler/version.rb +5 -0
- data/{Ruby/lib → lib}/mini_profiler_rails/railtie.rb +6 -2
- data/{Ruby/lib → lib}/patches/net_patches.rb +0 -0
- data/{Ruby/lib → lib}/patches/sql_patches.rb +3 -2
- data/{Ruby/lib → lib}/rack-mini-profiler.rb +0 -0
- data/rack-mini-profiler.gemspec +14 -6
- metadata +153 -43
- data/Ruby/lib/html/flamegraph.html +0 -351
- data/Ruby/lib/html/profile_handler.js +0 -1
- data/Ruby/lib/mini_profiler/flame_graph.rb +0 -54
- data/Ruby/lib/mini_profiler/gc_profiler.rb +0 -107
- data/Ruby/lib/mini_profiler/version.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 521860df9b65cce4c241d9fed2b68c671fcecdfe
|
4
|
+
data.tar.gz: bc1e39096a4c99ff1eabc31fa94bca57845dc95d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5b6273e7286a487549c2beea765802d30f6e27e7624dd4317c12cb028e8eaa97cac9914e8bb6bb53f12757f1002fa56d513a60adf57f7d3910875ff6784d7de
|
7
|
+
data.tar.gz: cbe7f735f443cb54c209170a32da9c45ded84d0b151e87ba1cc0f113c6cad393390430712e354c88215b7f88a69f540649be033fcdadfbbef44d9d0aacd4305d
|
@@ -143,5 +143,32 @@
|
|
143
143
|
?pp=env and others
|
144
144
|
* SOLR xml unescaped by mistake
|
145
145
|
|
146
|
+
20-August-2013
|
147
|
+
* 1.29
|
148
|
+
* Bugfix: SOLR patching had an incorrect monkey patch
|
149
|
+
* Implemented exception tracing using TracePoint see pp=trace-exceptions
|
150
|
+
|
151
|
+
30-August-2013
|
152
|
+
|
153
|
+
* 1.30
|
154
|
+
* Feature: Added Rack::MiniProfiler.counter_method(klass,name) for injecting counters
|
155
|
+
* Bug: Counters were not shifting the table correctly
|
156
|
+
|
157
|
+
3-September-2013
|
158
|
+
|
159
|
+
* Ripped out flamegraph so it can be isolated into a gem
|
160
|
+
* Flamegraph now has much increased fidelity
|
161
|
+
* Ripped out pp=sample it just was never really used
|
162
|
+
|
163
|
+
17-September-2013 - Ross Wilson
|
164
|
+
* Instead of supressing all "/assets/" requests we now check the configured
|
165
|
+
config.assets.prefix path since developers can rename the path to serve Asset Pipeline
|
166
|
+
files from
|
167
|
+
|
168
|
+
12-December-2013 - Sam Saffron
|
169
|
+
* Version 0.9.0.pre (bumped up to reflect the stability of the project)
|
170
|
+
* Improved reports for pp=profile-gc
|
171
|
+
* pp=flamegraph&flamegraph_sample_rate=1 , allow you to specify sampling rates
|
172
|
+
|
146
173
|
|
147
174
|
|
@@ -1,33 +1,38 @@
|
|
1
1
|
# rack-mini-profiler
|
2
2
|
|
3
|
+
[![Code Climate](https://codeclimate.com/github/MiniProfiler/rack-mini-profiler.png)](https://codeclimate.com/github/MiniProfiler/rack-mini-profiler) [![Build Status](https://travis-ci.org/MiniProfiler/rack-mini-profiler.png)](https://travis-ci.org/MiniProfiler/rack-mini-profiler)
|
4
|
+
|
3
5
|
Middleware that displays speed badge for every html page. Designed to work both in production and in development.
|
4
6
|
|
5
|
-
##
|
7
|
+
## rack-mini-profiler needs your help
|
8
|
+
|
9
|
+
We have decided to restructure our repository so there is a central UI repo and the various language implementation have their own.
|
10
|
+
|
11
|
+
The new home for rack-mini-profiler is https://github.com/MiniProfiler/rack-mini-profiler
|
12
|
+
|
13
|
+
**WE NEED HELP.**
|
14
|
+
|
15
|
+
- Setting up a build that reuses https://github.com/MiniProfiler/ui
|
16
|
+
- Migrating the internal data structures [per the spec](https://github.com/MiniProfiler/ui)
|
17
|
+
- Cleaning up the [horrendous class structure that is using strings as keys and crazy non-objects](https://github.com/MiniProfiler/rack-mini-profiler/blob/master/lib/mini_profiler/sql_timer_struct.rb#L36-L44)
|
18
|
+
|
19
|
+
If you feel like taking on any of this start an issue and update us on your progress.
|
20
|
+
|
21
|
+
## Installation
|
6
22
|
|
7
23
|
Install/add to Gemfile
|
8
24
|
|
9
25
|
```ruby
|
10
26
|
gem 'rack-mini-profiler'
|
11
27
|
```
|
12
|
-
Using Rails:
|
13
28
|
|
14
|
-
|
29
|
+
NOTE: Be sure to require rack_mini_profiler below the `pg` and `mysql` gems in your Gemfile. rack_mini_profiler will identify these gems if they are loaded to insert instrumentation. If included too early no SQL will show up.
|
15
30
|
|
16
|
-
|
31
|
+
#### Rails
|
17
32
|
|
18
|
-
|
33
|
+
All you have to do is include the Gem and you're good to go in development. See notes below for use in production.
|
19
34
|
|
20
|
-
|
21
|
-
# A hook in your ApplicationController
|
22
|
-
def authorize
|
23
|
-
if current_user.is_admin?
|
24
|
-
Rack::MiniProfiler.authorize_request
|
25
|
-
end
|
26
|
-
end
|
27
|
-
````
|
28
|
-
|
29
|
-
|
30
|
-
Using Builder:
|
35
|
+
#### Rack Builder
|
31
36
|
|
32
37
|
```ruby
|
33
38
|
require 'rack-mini-profiler'
|
@@ -38,7 +43,7 @@ builder = Rack::Builder.new do
|
|
38
43
|
end
|
39
44
|
```
|
40
45
|
|
41
|
-
|
46
|
+
#### Sinatra
|
42
47
|
|
43
48
|
```ruby
|
44
49
|
require 'rack-mini-profiler'
|
@@ -47,6 +52,19 @@ class MyApp < Sinatra::Base
|
|
47
52
|
end
|
48
53
|
```
|
49
54
|
|
55
|
+
## Using rack-mini-profiler in your app
|
56
|
+
|
57
|
+
rack-mini-profiler is designed with production profiling in mind. To enable that just run `Rack::MiniProfiler.authorize_request` once you know a request is allowed to profile.
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
# A hook in your ApplicationController
|
61
|
+
def authorize
|
62
|
+
if current_user.is_admin?
|
63
|
+
Rack::MiniProfiler.authorize_request
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
50
68
|
## Database profiling
|
51
69
|
|
52
70
|
Currently supports Mysql2, Postgres, and Mongoid3 (with fallback support to ActiveRecord)
|
@@ -61,7 +79,7 @@ There are 4 storage options: `MemoryStore`, `RedisStore`, `MemcacheStore`, and `
|
|
61
79
|
|
62
80
|
To change the default you can create a file in `config/initializers/mini_profiler.rb`
|
63
81
|
|
64
|
-
```ruby
|
82
|
+
```ruby
|
65
83
|
# set MemoryStore
|
66
84
|
Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
|
67
85
|
|
@@ -73,24 +91,24 @@ if Rails.env.production?
|
|
73
91
|
end
|
74
92
|
```
|
75
93
|
|
76
|
-
MemoryStore stores results in a processes heap - something that does not work well in a multi process environment.
|
77
|
-
FileStore stores results in the file system - something that may not work well in a multi machine environment.
|
94
|
+
MemoryStore stores results in a processes heap - something that does not work well in a multi process environment.
|
95
|
+
FileStore stores results in the file system - something that may not work well in a multi machine environment.
|
78
96
|
RedisStore/MemcacheStore work in multi process and multi machine environments (RedisStore only saves results for up to 24 hours so it won't continue to fill up Redis).
|
79
97
|
|
80
|
-
Additionally you may implement an AbstractStore for your own provider.
|
98
|
+
Additionally you may implement an AbstractStore for your own provider.
|
81
99
|
|
82
100
|
## User result segregation
|
83
101
|
|
84
|
-
MiniProfiler will attempt to keep all user results isolated, out-of-the-box the user provider uses the ip address:
|
102
|
+
MiniProfiler will attempt to keep all user results isolated, out-of-the-box the user provider uses the ip address:
|
85
103
|
|
86
104
|
```ruby
|
87
|
-
Rack::MiniProfiler.config.user_provider = Proc.new{|env| Rack::Request.new(env).ip}
|
105
|
+
Rack::MiniProfiler.config.user_provider = Proc.new{|env| Rack::Request.new(env).ip}
|
88
106
|
```
|
89
107
|
|
90
|
-
You can override (something that is very important in a multi-machine production setup):
|
108
|
+
You can override (something that is very important in a multi-machine production setup):
|
91
109
|
|
92
110
|
```ruby
|
93
|
-
Rack::MiniProfiler.config.user_provider = Proc.new{ |env| CurrentUser.get(env) }
|
111
|
+
Rack::MiniProfiler.config.user_provider = Proc.new{ |env| CurrentUser.get(env) }
|
94
112
|
```
|
95
113
|
|
96
114
|
The string this function returns should be unique for each user on the system (for anonymous you may need to fall back to ip address)
|
@@ -113,9 +131,13 @@ You can set configuration options using the configuration accessor on Rack::Mini
|
|
113
131
|
Rack::MiniProfiler.config.position = 'right'
|
114
132
|
# Have Mini Profiler start in hidden mode - display with short cut (defaulted to 'Alt+P')
|
115
133
|
Rack::MiniProfiler.config.start_hidden = true
|
134
|
+
# Have Rack::MiniProfiler start disabled - you can use query string option to re-enable later
|
135
|
+
Rack::MiniProfiler.config.enabled = false
|
116
136
|
# Don't collect backtraces on SQL queries that take less than 5 ms to execute
|
117
137
|
# (necessary on Rubies earlier than 2.0)
|
118
138
|
Rack::MiniProfiler.config.backtrace_threshold_ms = 5
|
139
|
+
# Set the sampling rate for flamegraph, in ms - defaults to 0.5ms
|
140
|
+
Rack::MiniProfiler.config.flamegraph_sample_rate = 1
|
119
141
|
```
|
120
142
|
|
121
143
|
|
@@ -151,10 +173,6 @@ if JSON.const_defined?(:Pure)
|
|
151
173
|
end
|
152
174
|
```
|
153
175
|
|
154
|
-
## Notes
|
155
|
-
|
156
|
-
- Be sure to require rack_mini_profiler last in your Gemfile, when it is required it will monkey patch pg and mysql gems to insert instrumentation. If included to early no SQL will show up.
|
157
|
-
|
158
176
|
## Available Options
|
159
177
|
|
160
178
|
* pre_authorize_cb - A lambda callback you can set to determine whether or not mini_profiler should be visible on a given request. Default in a Rails environment is only on in development mode. If in a Rack app, the default is always on.
|
@@ -165,8 +183,32 @@ end
|
|
165
183
|
* toggle_shortcut (default Alt+P) - a jquery.hotkeys.js-style keyboard shortcut, used to toggle the mini_profiler's visibility. See http://code.google.com/p/js-hotkeys/ for more info.
|
166
184
|
* start_hidden (default false) - Whether or not you want the mini_profiler to be visible when loading a page
|
167
185
|
* backtrace_threshold_ms (default zero) - Minimum SQL query elapsed time before a backtrace is recorded. Backtrace recording can take a couple of milliseconds on rubies earlier than 2.0, impacting performance for very small queries.
|
186
|
+
* flamegraph_sample_rate (default 0.5ms) - How often fast_stack should get stack trace info to generate flamegraphs
|
187
|
+
|
188
|
+
## Special query strings
|
189
|
+
|
190
|
+
If you include the query string `pp=help` at the end of your request you will see the various options available. You can use these options to extend or contract the amount of diagnostics rack-mini-profiler gathers.
|
191
|
+
|
192
|
+
## Licence
|
193
|
+
|
194
|
+
The MIT License (MIT)
|
195
|
+
|
196
|
+
Copyright (c) 2013 Sam Saffron
|
168
197
|
|
169
|
-
|
198
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
199
|
+
of this software and associated documentation files (the "Software"), to deal
|
200
|
+
in the Software without restriction, including without limitation the rights
|
201
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
202
|
+
copies of the Software, and to permit persons to whom the Software is
|
203
|
+
furnished to do so, subject to the following conditions:
|
170
204
|
|
171
|
-
|
205
|
+
The above copyright notice and this permission notice shall be included in
|
206
|
+
all copies or substantial portions of the Software.
|
172
207
|
|
208
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
209
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
210
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
211
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
212
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
213
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
214
|
+
THE SOFTWARE.
|
File without changes
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
"use strict";
|
2
2
|
var MiniProfiler = (function () {
|
3
3
|
var $;
|
4
4
|
|
@@ -659,6 +659,14 @@ var MiniProfiler = (function () {
|
|
659
659
|
} else {
|
660
660
|
doInit();
|
661
661
|
}
|
662
|
+
|
663
|
+
// jquery.hotkeys.js
|
664
|
+
// https://github.com/jeresig/jquery.hotkeys/blob/master/jquery.hotkeys.js
|
665
|
+
|
666
|
+
(function(d){function h(g){if("string"===typeof g.data){var h=g.handler,j=g.data.toLowerCase().split(" ");g.handler=function(b){if(!(this!==b.target&&(/textarea|select/i.test(b.target.nodeName)||"text"===b.target.type))){var c="keypress"!==b.type&&d.hotkeys.specialKeys[b.which],e=String.fromCharCode(b.which).toLowerCase(),a="",f={};b.altKey&&"alt"!==c&&(a+="alt+");b.ctrlKey&&"ctrl"!==c&&(a+="ctrl+");b.metaKey&&(!b.ctrlKey&&"meta"!==c)&&(a+="meta+");b.shiftKey&&"shift"!==c&&(a+="shift+");c?f[a+c]=
|
667
|
+
!0:(f[a+e]=!0,f[a+d.hotkeys.shiftNums[e]]=!0,"shift+"===a&&(f[d.hotkeys.shiftNums[e]]=!0));c=0;for(e=j.length;c<e;c++)if(f[j[c]])return h.apply(this,arguments)}}}}d.hotkeys={version:"0.8",specialKeys:{8:"backspace",9:"tab",13:"return",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"del",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",
|
668
|
+
109:"-",110:".",111:"/",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",144:"numlock",145:"scroll",191:"/",224:"meta"},shiftNums:{"`":"~",1:"!",2:"@",3:"#",4:"$",5:"%",6:"^",7:"&",8:"*",9:"(","0":")","-":"_","=":"+",";":": ","'":'"',",":"<",".":">","/":"?","\\":"|"}};d.each(["keydown","keyup","keypress"],function(){d.event.special[this]={add:h}})})(MiniProfiler.jQuery);
|
669
|
+
|
662
670
|
};
|
663
671
|
|
664
672
|
var major, minor;
|
@@ -901,13 +909,6 @@ var MiniProfiler = (function () {
|
|
901
909
|
|
902
910
|
MiniProfiler.init();
|
903
911
|
|
904
|
-
// jquery.hotkeys.js
|
905
|
-
// https://github.com/jeresig/jquery.hotkeys/blob/master/jquery.hotkeys.js
|
906
|
-
|
907
|
-
(function(d){function h(g){if("string"===typeof g.data){var h=g.handler,j=g.data.toLowerCase().split(" ");g.handler=function(b){if(!(this!==b.target&&(/textarea|select/i.test(b.target.nodeName)||"text"===b.target.type))){var c="keypress"!==b.type&&d.hotkeys.specialKeys[b.which],e=String.fromCharCode(b.which).toLowerCase(),a="",f={};b.altKey&&"alt"!==c&&(a+="alt+");b.ctrlKey&&"ctrl"!==c&&(a+="ctrl+");b.metaKey&&(!b.ctrlKey&&"meta"!==c)&&(a+="meta+");b.shiftKey&&"shift"!==c&&(a+="shift+");c?f[a+c]=
|
908
|
-
!0:(f[a+e]=!0,f[a+d.hotkeys.shiftNums[e]]=!0,"shift+"===a&&(f[d.hotkeys.shiftNums[e]]=!0));c=0;for(e=j.length;c<e;c++)if(f[j[c]])return h.apply(this,arguments)}}}}d.hotkeys={version:"0.8",specialKeys:{8:"backspace",9:"tab",13:"return",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"del",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",
|
909
|
-
109:"-",110:".",111:"/",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",144:"numlock",145:"scroll",191:"/",224:"meta"},shiftNums:{"`":"~",1:"!",2:"@",3:"#",4:"$",5:"%",6:"^",7:"&",8:"*",9:"(","0":")","-":"_","=":"+",";":": ","'":'"',",":"<",".":">","/":"?","\\":"|"}};d.each(["keydown","keyup","keypress"],function(){d.event.special[this]={add:h}})})(jQuery);
|
910
|
-
|
911
912
|
if (typeof prettyPrint === "undefined") {
|
912
913
|
|
913
914
|
// prettify.js
|
File without changes
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
<script id="profilerTemplate" type="text/x-jquery-tmpl">
|
2
2
|
|
3
3
|
<div class="profiler-result">
|
4
4
|
|
@@ -159,6 +159,8 @@
|
|
159
159
|
<td class="profiler-duration" title="aggregate duration of all queries in this step (excludes children)">
|
160
160
|
${MiniProfiler.formatDuration(timing.SqlTimingsDurationMilliseconds)}
|
161
161
|
</td>
|
162
|
+
{{else}}
|
163
|
+
<td colspan="2"></td>
|
162
164
|
{{/if}}
|
163
165
|
|
164
166
|
{{each page.CustomTimingNames}}
|
File without changes
|
File without changes
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
tbody tr:nth-child(odd) { background-color:#eee; }
|
2
2
|
tbody tr:nth-child(even) { background-color:#fff; }
|
3
3
|
table { border: 0; border-spacing:0;}
|
4
4
|
tr {border: 0;}
|
@@ -6,4 +6,4 @@ tr {border: 0;}
|
|
6
6
|
td {padding: 8px;}
|
7
7
|
.time {text-align:center;}
|
8
8
|
thead tr {background-color: #bbb; color: #444; font-size: 12px;}
|
9
|
-
thead tr th { padding: 5px 15px;}
|
9
|
+
thead tr th { padding: 5px 15px;}
|
@@ -0,0 +1 @@
|
|
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,4 +1,4 @@
|
|
1
|
-
|
1
|
+
<html>
|
2
2
|
<head>
|
3
3
|
<title>{name} ({duration} ms) - Profiling Results</title>
|
4
4
|
<script type='text/javascript' src='{path}jquery.1.7.1.js?v={version}'></script>
|
@@ -8,4 +8,4 @@
|
|
8
8
|
<body>
|
9
9
|
<div class='profiler-result-full'></div>
|
10
10
|
</body>
|
11
|
-
</html>
|
11
|
+
</html>
|
@@ -1,20 +1,20 @@
|
|
1
1
|
module Rack
|
2
2
|
class MiniProfiler
|
3
3
|
class ClientSettings
|
4
|
-
|
4
|
+
|
5
5
|
COOKIE_NAME = "__profilin"
|
6
6
|
|
7
7
|
BACKTRACE_DEFAULT = nil
|
8
|
-
BACKTRACE_FULL = 1
|
8
|
+
BACKTRACE_FULL = 1
|
9
9
|
BACKTRACE_NONE = 2
|
10
10
|
|
11
11
|
attr_accessor :disable_profiling
|
12
12
|
attr_accessor :backtrace_level
|
13
13
|
|
14
|
-
|
14
|
+
|
15
15
|
def initialize(env)
|
16
16
|
request = ::Rack::Request.new(env)
|
17
|
-
@cookie = request.cookies[COOKIE_NAME]
|
17
|
+
@cookie = request.cookies[COOKIE_NAME]
|
18
18
|
if @cookie
|
19
19
|
@cookie.split(",").map{|pair| pair.split("=")}.each do |k,v|
|
20
20
|
@orig_disable_profiling = @disable_profiling = (v=='t') if k == "dp"
|
@@ -22,7 +22,7 @@ module Rack
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
@backtrace_level = nil if !@backtrace_level.nil? && (@backtrace_level == 0 || @backtrace_level > BACKTRACE_NONE)
|
25
|
+
@backtrace_level = nil if !@backtrace_level.nil? && (@backtrace_level == 0 || @backtrace_level > BACKTRACE_NONE)
|
26
26
|
@orig_backtrace_level = @backtrace_level
|
27
27
|
|
28
28
|
end
|
@@ -30,7 +30,7 @@ module Rack
|
|
30
30
|
def write!(headers)
|
31
31
|
if @orig_disable_profiling != @disable_profiling || @orig_backtrace_level != @backtrace_level || @cookie.nil?
|
32
32
|
settings = {"p" => "t" }
|
33
|
-
settings["dp"] = "t" if @disable_profiling
|
33
|
+
settings["dp"] = "t" if @disable_profiling
|
34
34
|
settings["bt"] = @backtrace_level if @backtrace_level
|
35
35
|
settings_string = settings.map{|k,v| "#{k}=#{v}"}.join(",")
|
36
36
|
Rack::Utils.set_cookie_header!(headers, COOKIE_NAME, :value => settings_string, :path => '/')
|
@@ -45,19 +45,19 @@ module Rack
|
|
45
45
|
!@cookie.nil?
|
46
46
|
end
|
47
47
|
|
48
|
-
def disable_profiling?
|
48
|
+
def disable_profiling?
|
49
49
|
@disable_profiling
|
50
50
|
end
|
51
51
|
|
52
|
-
def backtrace_full?
|
52
|
+
def backtrace_full?
|
53
53
|
@backtrace_level == BACKTRACE_FULL
|
54
54
|
end
|
55
55
|
|
56
|
-
def backtrace_default?
|
56
|
+
def backtrace_default?
|
57
57
|
@backtrace_level == BACKTRACE_DEFAULT
|
58
58
|
end
|
59
|
-
|
60
|
-
def backtrace_none?
|
59
|
+
|
60
|
+
def backtrace_none?
|
61
61
|
@backtrace_level == BACKTRACE_NONE
|
62
62
|
end
|
63
63
|
end
|
File without changes
|
@@ -12,10 +12,10 @@ module Rack
|
|
12
12
|
@attributes
|
13
13
|
end
|
14
14
|
|
15
|
-
attr_accessor :auto_inject, :
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
attr_accessor :authorization_mode, :auto_inject, :backtrace_ignores, :backtrace_includes, :backtrace_remove,
|
16
|
+
:backtrace_threshold_ms, :base_url_path, :enabled, :flamegraph_sample_rate, :logger, :position,
|
17
|
+
:pre_authorize_cb, :skip_paths, :skip_schema_queries, :start_hidden, :storage, :storage_failure,
|
18
|
+
:storage_instance, :storage_options, :toggle_shortcut, :user_provider
|
19
19
|
|
20
20
|
# Deprecated options
|
21
21
|
attr_accessor :use_existing_jquery
|
@@ -37,6 +37,13 @@ module Rack
|
|
37
37
|
@toggle_shortcut = 'Alt+P'
|
38
38
|
@start_hidden = false
|
39
39
|
@backtrace_threshold_ms = 0
|
40
|
+
@flamegraph_sample_rate = 0.5
|
41
|
+
@storage_failure = Proc.new do |exception|
|
42
|
+
if @logger
|
43
|
+
@logger.warn("MiniProfiler storage failure: #{exception.message}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@enabled = true
|
40
47
|
self
|
41
48
|
}
|
42
49
|
end
|
File without changes
|
@@ -0,0 +1,181 @@
|
|
1
|
+
class Rack::MiniProfiler::GCProfiler
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
@ignore = []
|
5
|
+
@ignore << @ignore.__id__
|
6
|
+
end
|
7
|
+
|
8
|
+
def object_space_stats
|
9
|
+
stats = {}
|
10
|
+
ids = {}
|
11
|
+
|
12
|
+
@ignore << stats.__id__
|
13
|
+
@ignore << ids.__id__
|
14
|
+
|
15
|
+
i=0
|
16
|
+
ObjectSpace.each_object { |o|
|
17
|
+
begin
|
18
|
+
i = stats[o.class] || 0
|
19
|
+
i += 1
|
20
|
+
stats[o.class] = i
|
21
|
+
ids[o.__id__] = o if Integer === o.__id__
|
22
|
+
rescue NoMethodError
|
23
|
+
# protect against BasicObject
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
@ignore.each do |id|
|
28
|
+
if ids.delete(id)
|
29
|
+
klass = ObjectSpace._id2ref(id).class
|
30
|
+
stats[klass] -= 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
result = {:stats => stats, :ids => ids}
|
35
|
+
@ignore << result.__id__
|
36
|
+
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
def diff_object_stats(before,after)
|
41
|
+
diff = {}
|
42
|
+
after.each do |k,v|
|
43
|
+
diff[k] = v - (before[k] || 0)
|
44
|
+
end
|
45
|
+
before.each do |k,v|
|
46
|
+
diff[k] = 0 - v unless after[k]
|
47
|
+
end
|
48
|
+
|
49
|
+
diff
|
50
|
+
end
|
51
|
+
|
52
|
+
def analyze_strings(ids_before,ids_after)
|
53
|
+
result = {}
|
54
|
+
ids_after.each do |id,_|
|
55
|
+
obj = ObjectSpace._id2ref(id)
|
56
|
+
if String === obj && !ids_before.include?(obj.object_id)
|
57
|
+
result[obj] ||= 0
|
58
|
+
result[obj] += 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
64
|
+
def analyze_growth(ids_before, ids_after)
|
65
|
+
new_objects = 0
|
66
|
+
memory_allocated = 0
|
67
|
+
|
68
|
+
ids_after.each do |id,_|
|
69
|
+
if !ids_before.include?(id) && obj=ObjectSpace._id2ref(id)
|
70
|
+
# this is going to be version specific (may change in 2.1)
|
71
|
+
size = ObjectSpace.memsize_of(obj)
|
72
|
+
memory_allocated += size
|
73
|
+
new_objects += 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
[new_objects, memory_allocated]
|
78
|
+
end
|
79
|
+
|
80
|
+
def analyze_initial_state(ids_before)
|
81
|
+
memory_allocated = 0
|
82
|
+
objects = 0
|
83
|
+
|
84
|
+
ids_before.each do |id,_|
|
85
|
+
if obj=ObjectSpace._id2ref(id)
|
86
|
+
# this is going to be version specific (may change in 2.1)
|
87
|
+
memory_allocated += ObjectSpace.memsize_of(obj)
|
88
|
+
objects += 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
[objects,memory_allocated]
|
93
|
+
end
|
94
|
+
|
95
|
+
def profile_gc_time(app,env)
|
96
|
+
body = []
|
97
|
+
|
98
|
+
begin
|
99
|
+
GC::Profiler.clear
|
100
|
+
prev_profiler_state = GC::Profiler.enabled?
|
101
|
+
prev_gc_state = GC.enable
|
102
|
+
GC::Profiler.enable
|
103
|
+
b = app.call(env)[2]
|
104
|
+
b.close if b.respond_to? :close
|
105
|
+
body << "GC Profiler ran during this request, if it fired you will see the cost below:\n\n"
|
106
|
+
body << GC::Profiler.result
|
107
|
+
ensure
|
108
|
+
prev_gc_state ? GC.disable : GC.enable
|
109
|
+
GC::Profiler.disable unless prev_profiler_state
|
110
|
+
end
|
111
|
+
|
112
|
+
return [200, {'Content-Type' => 'text/plain'}, body]
|
113
|
+
end
|
114
|
+
|
115
|
+
def profile_gc(app,env)
|
116
|
+
|
117
|
+
# for memsize_of
|
118
|
+
require 'objspace'
|
119
|
+
|
120
|
+
body = [];
|
121
|
+
|
122
|
+
stat_before,stat_after,diff,string_analysis,
|
123
|
+
new_objects, memory_allocated, stat, memory_before, objects_before = nil
|
124
|
+
|
125
|
+
# clean up before
|
126
|
+
GC.start
|
127
|
+
stat = GC.stat
|
128
|
+
prev_gc_state = GC.disable
|
129
|
+
stat_before = object_space_stats
|
130
|
+
b = app.call(env)[2]
|
131
|
+
b.close if b.respond_to? :close
|
132
|
+
stat_after = object_space_stats
|
133
|
+
# so we don't blow out on memory
|
134
|
+
prev_gc_state ? GC.disable : GC.enable
|
135
|
+
|
136
|
+
diff = diff_object_stats(stat_before[:stats],stat_after[:stats])
|
137
|
+
string_analysis = analyze_strings(stat_before[:ids], stat_after[:ids])
|
138
|
+
new_objects, memory_allocated = analyze_growth(stat_before[:ids], stat_after[:ids])
|
139
|
+
objects_before, memory_before = analyze_initial_state(stat_before[:ids])
|
140
|
+
|
141
|
+
|
142
|
+
body << "
|
143
|
+
Overview
|
144
|
+
------------------------------------
|
145
|
+
Initial state: object count - #{objects_before} , memory allocated outside heap (bytes) #{memory_before}
|
146
|
+
|
147
|
+
GC Stats: #{stat.map{|k,v| "#{k} : #{v}" }.join(", ")}
|
148
|
+
|
149
|
+
New bytes allocated outside of Ruby heaps: #{memory_allocated}
|
150
|
+
New objects: #{new_objects}
|
151
|
+
"
|
152
|
+
|
153
|
+
body << "
|
154
|
+
ObjectSpace delta caused by request:
|
155
|
+
--------------------------------------------\n"
|
156
|
+
diff.to_a.reject{|k,v| v == 0}.sort{|x,y| y[1] <=> x[1]}.each do |k,v|
|
157
|
+
body << "#{k} : #{v}\n" if v != 0
|
158
|
+
end
|
159
|
+
|
160
|
+
body << "\n
|
161
|
+
ObjectSpace stats:
|
162
|
+
-----------------\n"
|
163
|
+
|
164
|
+
stat_after[:stats].to_a.sort{|x,y| y[1] <=> x[1]}.each do |k,v|
|
165
|
+
body << "#{k} : #{v}\n"
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
body << "\n
|
170
|
+
String stats:
|
171
|
+
------------\n"
|
172
|
+
|
173
|
+
string_analysis.to_a.sort{|x,y| y[1] <=> x[1] }.take(1000).each do |string,count|
|
174
|
+
body << "#{count} : #{string}\n"
|
175
|
+
end
|
176
|
+
|
177
|
+
return [200, {'Content-Type' => 'text/plain'}, body]
|
178
|
+
ensure
|
179
|
+
prev_gc_state ? GC.disable : GC.enable
|
180
|
+
end
|
181
|
+
end
|