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.

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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dd8393bb0afbc59162f9ece7f63b47218f742127
4
- data.tar.gz: 4f29c3a0407eb08c0b5146eea018919dc6620db8
3
+ metadata.gz: 521860df9b65cce4c241d9fed2b68c671fcecdfe
4
+ data.tar.gz: bc1e39096a4c99ff1eabc31fa94bca57845dc95d
5
5
  SHA512:
6
- metadata.gz: 0276de7ece6f3f8a70a4ce085ddb36a209385b1e3892c147057d6629922b4ea90e33177fc1093c72c08bf2de156585cff6b95576733c1cc906cf7cd4cd16000f
7
- data.tar.gz: f9accaad19036dde64fdacb1c86ddd15892d97e778ce079bd579bdbe78df508b41716fecc17e0b3176887002caef63f6cc9f37c6384f12ecc233bb170a384863
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
- ## Using rack-mini-profiler in your app
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
- All you have to do is include the Gem and you're good to go in development.
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
- 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.
31
+ #### Rails
17
32
 
18
- Using Rails:
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
- ```ruby
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
- Using Sinatra:
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
- ## Special query strings
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
- 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.
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
- "use strict";
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
- <script id="profilerTemplate" type="text/x-jquery-tmpl">
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
- tbody tr:nth-child(odd) { background-color:#eee; }
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;}
@@ -1,4 +1,4 @@
1
- var MiniProfiler = MiniProfiler || {};
1
+ var MiniProfiler = MiniProfiler || {};
2
2
  MiniProfiler.list = {
3
3
  init:
4
4
  function (options) {
@@ -1,4 +1,4 @@
1
- <script id="tableTemplate" type="text/x-jquery-tmpl">
1
+ <script id="tableTemplate" type="text/x-jquery-tmpl">
2
2
  <table>
3
3
  <thead>
4
4
  <tr>
@@ -31,4 +31,4 @@
31
31
  <td colspan="3"></td>
32
32
  {{/if}}
33
33
  </tr>
34
- </script>
34
+ </script>
@@ -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
- <html>
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
@@ -12,10 +12,10 @@ module Rack
12
12
  @attributes
13
13
  end
14
14
 
15
- attr_accessor :auto_inject, :base_url_path, :pre_authorize_cb, :position,
16
- :backtrace_remove, :backtrace_includes, :backtrace_ignores, :skip_schema_queries,
17
- :storage, :user_provider, :storage_instance, :storage_options, :skip_paths, :authorization_mode,
18
- :toggle_shortcut, :start_hidden, :backtrace_threshold_ms
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
@@ -1,6 +1,6 @@
1
1
  class Rack::MiniProfiler::Context
2
2
  attr_accessor :inject_js,:current_timer,:page_struct,:skip_backtrace,:full_backtrace,:discard, :mpt_init, :measure
3
-
3
+
4
4
  def initialize(opts = {})
5
5
  opts["measure"] = true unless opts.key? "measure"
6
6
  opts.each do |k,v|
@@ -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