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.
- 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
|
+
[](https://codeclimate.com/github/MiniProfiler/rack-mini-profiler) [](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
|