miniprofiler 0.1.7.1
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.
- data/.gitignore +4 -0
 - data/CHANGELOG +32 -0
 - data/Gemfile +15 -0
 - data/Gemfile.lock +64 -0
 - data/README.md +110 -0
 - data/Rakefile +40 -0
 - data/autotest/discover.rb +2 -0
 - data/lib/mini_profiler/body_add_proxy.rb +45 -0
 - data/lib/mini_profiler/client_timer_struct.rb +76 -0
 - data/lib/mini_profiler/config.rb +52 -0
 - data/lib/mini_profiler/context.rb +10 -0
 - data/lib/mini_profiler/page_timer_struct.rb +53 -0
 - data/lib/mini_profiler/profiler.rb +405 -0
 - data/lib/mini_profiler/profiling_methods.rb +73 -0
 - data/lib/mini_profiler/request_timer_struct.rb +96 -0
 - data/lib/mini_profiler/sql_timer_struct.rb +48 -0
 - data/lib/mini_profiler/storage/abstract_store.rb +27 -0
 - data/lib/mini_profiler/storage/file_store.rb +108 -0
 - data/lib/mini_profiler/storage/memory_store.rb +68 -0
 - data/lib/mini_profiler/storage/redis_store.rb +44 -0
 - data/lib/mini_profiler/timer_struct.rb +31 -0
 - data/lib/mini_profiler_rails/railtie.rb +84 -0
 - data/lib/patches/sql_patches.rb +185 -0
 - data/lib/rack-mini-profiler.rb +6 -0
 - data/rack-mini-profiler.gemspec +23 -0
 - data/spec/components/body_add_proxy_spec.rb +90 -0
 - data/spec/components/client_timer_struct_spec.rb +165 -0
 - data/spec/components/file_store_spec.rb +47 -0
 - data/spec/components/memory_store_spec.rb +40 -0
 - data/spec/components/page_timer_struct_spec.rb +33 -0
 - data/spec/components/profiler_spec.rb +126 -0
 - data/spec/components/redis_store_spec.rb +44 -0
 - data/spec/components/request_timer_struct_spec.rb +148 -0
 - data/spec/components/sql_timer_struct_spec.rb +63 -0
 - data/spec/components/timer_struct_spec.rb +47 -0
 - data/spec/fixtures/simple_client_request.yml +17 -0
 - data/spec/fixtures/weird_client_request.yml +13 -0
 - data/spec/integration/mini_profiler_spec.rb +142 -0
 - data/spec/spec_helper.rb +31 -0
 - data/spec/support/expand_each_to_matcher.rb +8 -0
 - data/test_old/config.ru +41 -0
 - metadata +155 -0
 
| 
         @@ -0,0 +1,405 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'timeout'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'thread'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'mini_profiler/page_timer_struct'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'mini_profiler/sql_timer_struct'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'mini_profiler/client_timer_struct'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'mini_profiler/request_timer_struct'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require 'mini_profiler/body_add_proxy'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'mini_profiler/storage/abstract_store'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require 'mini_profiler/storage/memory_store'
         
     | 
| 
      
 12 
     | 
    
         
            +
            require 'mini_profiler/storage/redis_store'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require 'mini_profiler/storage/file_store'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'mini_profiler/config'
         
     | 
| 
      
 15 
     | 
    
         
            +
            require 'mini_profiler/profiling_methods'
         
     | 
| 
      
 16 
     | 
    
         
            +
            require 'mini_profiler/context'
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            module Rack
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            	class MiniProfiler
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            		VERSION = '104'.freeze
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                class << self 
         
     | 
| 
      
 25 
     | 
    
         
            +
                  
         
     | 
| 
      
 26 
     | 
    
         
            +
                  include Rack::MiniProfiler::ProfilingMethods
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def generate_id
         
     | 
| 
      
 29 
     | 
    
         
            +
                    rand(36**20).to_s(36)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def reset_config
         
     | 
| 
      
 33 
     | 
    
         
            +
                    @config = Config.default
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  # So we can change the configuration if we want
         
     | 
| 
      
 37 
     | 
    
         
            +
                  def config
         
     | 
| 
      
 38 
     | 
    
         
            +
                    @config ||= Config.default
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  def share_template
         
     | 
| 
      
 42 
     | 
    
         
            +
                    return @share_template unless @share_template.nil?
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @share_template = ::File.read(::File.expand_path("../html/share.html", ::File.dirname(__FILE__)))
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  
         
     | 
| 
      
 46 
     | 
    
         
            +
                  def current
         
     | 
| 
      
 47 
     | 
    
         
            +
                    Thread.current[:mini_profiler_private]
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  def current=(c)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    # we use TLS cause we need access to this from sql blocks and code blocks that have no access to env
         
     | 
| 
      
 52 
     | 
    
         
            +
                    Thread.current[:mini_profiler_private]= c
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  # discard existing results, don't track this request
         
     | 
| 
      
 56 
     | 
    
         
            +
                  def discard_results
         
     | 
| 
      
 57 
     | 
    
         
            +
                    self.current.discard = true if current
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  # user has the mini profiler cookie, only used when config.authorization_mode == :whitelist
         
     | 
| 
      
 61 
     | 
    
         
            +
                  def has_profiling_cookie?(env)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    env['HTTP_COOKIE'] && env['HTTP_COOKIE'].include?("__profilin=stylin")
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  # remove the mini profiler cookie, only used when config.authorization_mode == :whitelist
         
     | 
| 
      
 66 
     | 
    
         
            +
                  def remove_profiling_cookie(headers)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    Rack::Utils.delete_cookie_header!(headers, '__profilin')
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  def set_profiling_cookie(headers)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    Rack::Utils.set_cookie_header!(headers, '__profilin', 'stylin')
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  def create_current(env={}, options={})
         
     | 
| 
      
 75 
     | 
    
         
            +
                    # profiling the request
         
     | 
| 
      
 76 
     | 
    
         
            +
                    self.current = Context.new
         
     | 
| 
      
 77 
     | 
    
         
            +
                    self.current.inject_js = config.auto_inject && (!env['HTTP_X_REQUESTED_WITH'].eql? 'XMLHttpRequest')
         
     | 
| 
      
 78 
     | 
    
         
            +
                    self.current.page_struct = PageTimerStruct.new(env)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    self.current.current_timer = current.page_struct['Root']
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def authorize_request
         
     | 
| 
      
 83 
     | 
    
         
            +
                    Thread.current[:mp_authorized] = true
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                  def deauthorize_request
         
     | 
| 
      
 87 
     | 
    
         
            +
                    Thread.current[:mp_authorized] = nil
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  def request_authorized?
         
     | 
| 
      
 91 
     | 
    
         
            +
                    Thread.current[:mp_authorized]
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
            		#
         
     | 
| 
      
 96 
     | 
    
         
            +
            		# options:
         
     | 
| 
      
 97 
     | 
    
         
            +
            		# :auto_inject - should script be automatically injected on every html page (not xhr)
         
     | 
| 
      
 98 
     | 
    
         
            +
            		def initialize(app, config = nil)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  MiniProfiler.config.merge!(config)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  @config = MiniProfiler.config 
         
     | 
| 
      
 101 
     | 
    
         
            +
            			@app = app
         
     | 
| 
      
 102 
     | 
    
         
            +
            			@config.base_url_path << "/" unless @config.base_url_path.end_with? "/"
         
     | 
| 
      
 103 
     | 
    
         
            +
                  unless @config.storage_instance
         
     | 
| 
      
 104 
     | 
    
         
            +
                    @storage = @config.storage_instance = @config.storage.new(@config.storage_options)
         
     | 
| 
      
 105 
     | 
    
         
            +
                  end
         
     | 
| 
      
 106 
     | 
    
         
            +
            		end
         
     | 
| 
      
 107 
     | 
    
         
            +
                
         
     | 
| 
      
 108 
     | 
    
         
            +
                def user(env)
         
     | 
| 
      
 109 
     | 
    
         
            +
                  @config.user_provider.call(env)
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
            		def serve_results(env)
         
     | 
| 
      
 113 
     | 
    
         
            +
            			request = Rack::Request.new(env)      
         
     | 
| 
      
 114 
     | 
    
         
            +
                  id = request['id']
         
     | 
| 
      
 115 
     | 
    
         
            +
            			page_struct = @storage.load(id)
         
     | 
| 
      
 116 
     | 
    
         
            +
                  unless page_struct
         
     | 
| 
      
 117 
     | 
    
         
            +
                    @storage.set_viewed(user(env), id) 
         
     | 
| 
      
 118 
     | 
    
         
            +
                    return [404, {}, ["Request not found: #{request['id']} - user #{user(env)}"]] 
         
     | 
| 
      
 119 
     | 
    
         
            +
                  end
         
     | 
| 
      
 120 
     | 
    
         
            +
            			unless page_struct['HasUserViewed']
         
     | 
| 
      
 121 
     | 
    
         
            +
            				page_struct['ClientTimings'].init_from_form_data(env, page_struct)
         
     | 
| 
      
 122 
     | 
    
         
            +
            				page_struct['HasUserViewed'] = true
         
     | 
| 
      
 123 
     | 
    
         
            +
                    @storage.save(page_struct) 
         
     | 
| 
      
 124 
     | 
    
         
            +
                    @storage.set_viewed(user(env), id) 
         
     | 
| 
      
 125 
     | 
    
         
            +
            			end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  result_json = page_struct.to_json
         
     | 
| 
      
 128 
     | 
    
         
            +
                  # If we're an XMLHttpRequest, serve up the contents as JSON
         
     | 
| 
      
 129 
     | 
    
         
            +
                  if request.xhr?
         
     | 
| 
      
 130 
     | 
    
         
            +
              			[200, { 'Content-Type' => 'application/json'}, [result_json]]
         
     | 
| 
      
 131 
     | 
    
         
            +
                  else
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                    # Otherwise give the HTML back
         
     | 
| 
      
 134 
     | 
    
         
            +
                    html = MiniProfiler.share_template.dup  
         
     | 
| 
      
 135 
     | 
    
         
            +
                    html.gsub!(/\{path\}/, @config.base_url_path)      
         
     | 
| 
      
 136 
     | 
    
         
            +
                    html.gsub!(/\{version\}/, MiniProfiler::VERSION)      
         
     | 
| 
      
 137 
     | 
    
         
            +
                    html.gsub!(/\{json\}/, result_json)
         
     | 
| 
      
 138 
     | 
    
         
            +
                    html.gsub!(/\{includes\}/, get_profile_script(env))
         
     | 
| 
      
 139 
     | 
    
         
            +
                    html.gsub!(/\{name\}/, page_struct['Name'])
         
     | 
| 
      
 140 
     | 
    
         
            +
                    html.gsub!(/\{duration\}/, page_struct.duration_ms.round(1).to_s)
         
     | 
| 
      
 141 
     | 
    
         
            +
                    
         
     | 
| 
      
 142 
     | 
    
         
            +
                    [200, {'Content-Type' => 'text/html'}, [html]]
         
     | 
| 
      
 143 
     | 
    
         
            +
                  end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
            		end
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
            		def serve_html(env)
         
     | 
| 
      
 148 
     | 
    
         
            +
            			file_name = env['PATH_INFO'][(@config.base_url_path.length)..1000]
         
     | 
| 
      
 149 
     | 
    
         
            +
            			return serve_results(env) if file_name.eql?('results')
         
     | 
| 
      
 150 
     | 
    
         
            +
            			full_path = ::File.expand_path("../html/#{file_name}", ::File.dirname(__FILE__))
         
     | 
| 
      
 151 
     | 
    
         
            +
            			return [404, {}, ["Not found"]] unless ::File.exists? full_path
         
     | 
| 
      
 152 
     | 
    
         
            +
            			f = Rack::File.new nil
         
     | 
| 
      
 153 
     | 
    
         
            +
            			f.path = full_path
         
     | 
| 
      
 154 
     | 
    
         
            +
            			f.cache_control = "max-age:86400"
         
     | 
| 
      
 155 
     | 
    
         
            +
            			f.serving env
         
     | 
| 
      
 156 
     | 
    
         
            +
            		end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                
         
     | 
| 
      
 159 
     | 
    
         
            +
                def current
         
     | 
| 
      
 160 
     | 
    
         
            +
                  MiniProfiler.current
         
     | 
| 
      
 161 
     | 
    
         
            +
                end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                def current=(c)
         
     | 
| 
      
 164 
     | 
    
         
            +
                  MiniProfiler.current=c
         
     | 
| 
      
 165 
     | 
    
         
            +
                end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                def config
         
     | 
| 
      
 169 
     | 
    
         
            +
                  @config
         
     | 
| 
      
 170 
     | 
    
         
            +
                end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
            		def call(env)
         
     | 
| 
      
 174 
     | 
    
         
            +
                  status = headers = body = nil
         
     | 
| 
      
 175 
     | 
    
         
            +
                  path = env['PATH_INFO']
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                  skip_it = (@config.pre_authorize_cb && !@config.pre_authorize_cb.call(env)) ||
         
     | 
| 
      
 178 
     | 
    
         
            +
                            (@config.skip_paths && @config.skip_paths.any?{ |p| path[0,p.length] == p}) ||
         
     | 
| 
      
 179 
     | 
    
         
            +
                            env["QUERY_STRING"] =~ /pp=skip/ 
         
     | 
| 
      
 180 
     | 
    
         
            +
                  
         
     | 
| 
      
 181 
     | 
    
         
            +
                  has_profiling_cookie = MiniProfiler.has_profiling_cookie?(env)
         
     | 
| 
      
 182 
     | 
    
         
            +
                
         
     | 
| 
      
 183 
     | 
    
         
            +
                  if skip_it || (@config.authorization_mode == :whitelist && !has_profiling_cookie)
         
     | 
| 
      
 184 
     | 
    
         
            +
                    status,headers,body = @app.call(env)
         
     | 
| 
      
 185 
     | 
    
         
            +
                    if !skip_it && @config.authorization_mode == :whitelist && !has_profiling_cookie && MiniProfiler.request_authorized? 
         
     | 
| 
      
 186 
     | 
    
         
            +
                      MiniProfiler.set_profiling_cookie(headers) 
         
     | 
| 
      
 187 
     | 
    
         
            +
                    end
         
     | 
| 
      
 188 
     | 
    
         
            +
                    return [status,headers,body]
         
     | 
| 
      
 189 
     | 
    
         
            +
                  end
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                  # handle all /mini-profiler requests here
         
     | 
| 
      
 192 
     | 
    
         
            +
            			return serve_html(env) if env['PATH_INFO'].start_with? @config.base_url_path
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                  MiniProfiler.create_current(env, @config)
         
     | 
| 
      
 195 
     | 
    
         
            +
                  MiniProfiler.deauthorize_request if @config.authorization_mode == :whitelist
         
     | 
| 
      
 196 
     | 
    
         
            +
                  if env["QUERY_STRING"] =~ /pp=no-backtrace/
         
     | 
| 
      
 197 
     | 
    
         
            +
                    current.skip_backtrace = true
         
     | 
| 
      
 198 
     | 
    
         
            +
                  elsif env["QUERY_STRING"] =~ /pp=full-backtrace/
         
     | 
| 
      
 199 
     | 
    
         
            +
                    current.full_backtrace = true
         
     | 
| 
      
 200 
     | 
    
         
            +
                  end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                  done_sampling = false
         
     | 
| 
      
 203 
     | 
    
         
            +
                  quit_sampler = false
         
     | 
| 
      
 204 
     | 
    
         
            +
                  backtraces = nil
         
     | 
| 
      
 205 
     | 
    
         
            +
                  missing_stacktrace = false
         
     | 
| 
      
 206 
     | 
    
         
            +
                  if env["QUERY_STRING"] =~ /pp=sample/
         
     | 
| 
      
 207 
     | 
    
         
            +
                    backtraces = []
         
     | 
| 
      
 208 
     | 
    
         
            +
                    t = Thread.current
         
     | 
| 
      
 209 
     | 
    
         
            +
                    Thread.new {
         
     | 
| 
      
 210 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 211 
     | 
    
         
            +
                        require 'stacktrace' rescue nil
         
     | 
| 
      
 212 
     | 
    
         
            +
                        if !t.respond_to? :stacktrace
         
     | 
| 
      
 213 
     | 
    
         
            +
                          missing_stacktrace = true
         
     | 
| 
      
 214 
     | 
    
         
            +
                          quit_sampler = true 
         
     | 
| 
      
 215 
     | 
    
         
            +
                          return
         
     | 
| 
      
 216 
     | 
    
         
            +
                        end
         
     | 
| 
      
 217 
     | 
    
         
            +
                        i = 10000 # for sanity never grab more than 10k samples 
         
     | 
| 
      
 218 
     | 
    
         
            +
                        while i > 0
         
     | 
| 
      
 219 
     | 
    
         
            +
                          break if done_sampling
         
     | 
| 
      
 220 
     | 
    
         
            +
                          i -= 1
         
     | 
| 
      
 221 
     | 
    
         
            +
                          backtraces << t.stacktrace
         
     | 
| 
      
 222 
     | 
    
         
            +
                          sleep 0.001
         
     | 
| 
      
 223 
     | 
    
         
            +
                        end
         
     | 
| 
      
 224 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 225 
     | 
    
         
            +
                        quit_sampler = true
         
     | 
| 
      
 226 
     | 
    
         
            +
                      end
         
     | 
| 
      
 227 
     | 
    
         
            +
                    }
         
     | 
| 
      
 228 
     | 
    
         
            +
                  end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
            			status, headers, body = nil
         
     | 
| 
      
 231 
     | 
    
         
            +
                  start = Time.now 
         
     | 
| 
      
 232 
     | 
    
         
            +
                  begin 
         
     | 
| 
      
 233 
     | 
    
         
            +
                    status,headers,body = @app.call(env)
         
     | 
| 
      
 234 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 235 
     | 
    
         
            +
                    if backtraces 
         
     | 
| 
      
 236 
     | 
    
         
            +
                      done_sampling = true
         
     | 
| 
      
 237 
     | 
    
         
            +
                      sleep 0.001 until quit_sampler
         
     | 
| 
      
 238 
     | 
    
         
            +
                    end
         
     | 
| 
      
 239 
     | 
    
         
            +
                  end
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
                  skip_it = current.discard
         
     | 
| 
      
 242 
     | 
    
         
            +
                  if (config.authorization_mode == :whitelist && !MiniProfiler.request_authorized?)
         
     | 
| 
      
 243 
     | 
    
         
            +
                    MiniProfiler.remove_profiling_cookie(headers)
         
     | 
| 
      
 244 
     | 
    
         
            +
                    skip_it = true
         
     | 
| 
      
 245 
     | 
    
         
            +
                  end
         
     | 
| 
      
 246 
     | 
    
         
            +
                  
         
     | 
| 
      
 247 
     | 
    
         
            +
                  return [status,headers,body] if skip_it
         
     | 
| 
      
 248 
     | 
    
         
            +
                  
         
     | 
| 
      
 249 
     | 
    
         
            +
                  # we must do this here, otherwise current[:discard] is not being properly treated
         
     | 
| 
      
 250 
     | 
    
         
            +
                  if env["QUERY_STRING"] =~ /pp=env/
         
     | 
| 
      
 251 
     | 
    
         
            +
                    body.close if body.respond_to? :close
         
     | 
| 
      
 252 
     | 
    
         
            +
                    return dump_env env
         
     | 
| 
      
 253 
     | 
    
         
            +
                  end
         
     | 
| 
      
 254 
     | 
    
         
            +
             
     | 
| 
      
 255 
     | 
    
         
            +
                  if env["QUERY_STRING"] =~ /pp=help/
         
     | 
| 
      
 256 
     | 
    
         
            +
                    body.close if body.respond_to? :close
         
     | 
| 
      
 257 
     | 
    
         
            +
                    return help
         
     | 
| 
      
 258 
     | 
    
         
            +
                  end
         
     | 
| 
      
 259 
     | 
    
         
            +
                  
         
     | 
| 
      
 260 
     | 
    
         
            +
                  page_struct = current.page_struct
         
     | 
| 
      
 261 
     | 
    
         
            +
            			page_struct['Root'].record_time((Time.now - start) * 1000)
         
     | 
| 
      
 262 
     | 
    
         
            +
             
     | 
| 
      
 263 
     | 
    
         
            +
                  if backtraces
         
     | 
| 
      
 264 
     | 
    
         
            +
                    body.close if body.respond_to? :close
         
     | 
| 
      
 265 
     | 
    
         
            +
                    return help(:stacktrace) if missing_stacktrace
         
     | 
| 
      
 266 
     | 
    
         
            +
                    return analyze(backtraces, page_struct)
         
     | 
| 
      
 267 
     | 
    
         
            +
                  end
         
     | 
| 
      
 268 
     | 
    
         
            +
                  
         
     | 
| 
      
 269 
     | 
    
         
            +
             
     | 
| 
      
 270 
     | 
    
         
            +
                  # no matter what it is, it should be unviewed, otherwise we will miss POST
         
     | 
| 
      
 271 
     | 
    
         
            +
                  @storage.set_unviewed(user(env), page_struct['Id']) 
         
     | 
| 
      
 272 
     | 
    
         
            +
            			@storage.save(page_struct)
         
     | 
| 
      
 273 
     | 
    
         
            +
            			
         
     | 
| 
      
 274 
     | 
    
         
            +
                  # inject headers, script
         
     | 
| 
      
 275 
     | 
    
         
            +
            			if status == 200
         
     | 
| 
      
 276 
     | 
    
         
            +
                    
         
     | 
| 
      
 277 
     | 
    
         
            +
            				# inject header
         
     | 
| 
      
 278 
     | 
    
         
            +
                    if headers.is_a? Hash
         
     | 
| 
      
 279 
     | 
    
         
            +
                      headers['X-MiniProfiler-Ids'] = ids_json(env)
         
     | 
| 
      
 280 
     | 
    
         
            +
                    end
         
     | 
| 
      
 281 
     | 
    
         
            +
             
     | 
| 
      
 282 
     | 
    
         
            +
            				# inject script
         
     | 
| 
      
 283 
     | 
    
         
            +
            				if current.inject_js \
         
     | 
| 
      
 284 
     | 
    
         
            +
            					&& headers.has_key?('Content-Type') \
         
     | 
| 
      
 285 
     | 
    
         
            +
            					&& !headers['Content-Type'].match(/text\/html/).nil? then
         
     | 
| 
      
 286 
     | 
    
         
            +
            					body = MiniProfiler::BodyAddProxy.new(body, self.get_profile_script(env))
         
     | 
| 
      
 287 
     | 
    
         
            +
            				end
         
     | 
| 
      
 288 
     | 
    
         
            +
            			end
         
     | 
| 
      
 289 
     | 
    
         
            +
             
     | 
| 
      
 290 
     | 
    
         
            +
                  # mini profiler is meddling with stuff, we can not cache cause we will get incorrect data
         
     | 
| 
      
 291 
     | 
    
         
            +
                  # Rack::ETag has already inserted some nonesense in the chain
         
     | 
| 
      
 292 
     | 
    
         
            +
                  headers.delete('ETag')
         
     | 
| 
      
 293 
     | 
    
         
            +
                  headers.delete('Date')
         
     | 
| 
      
 294 
     | 
    
         
            +
                  headers['Cache-Control'] = 'must-revalidate, private, max-age=0'
         
     | 
| 
      
 295 
     | 
    
         
            +
            			[status, headers, body]
         
     | 
| 
      
 296 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 297 
     | 
    
         
            +
                  # Make sure this always happens
         
     | 
| 
      
 298 
     | 
    
         
            +
                  current = nil
         
     | 
| 
      
 299 
     | 
    
         
            +
            		end
         
     | 
| 
      
 300 
     | 
    
         
            +
             
     | 
| 
      
 301 
     | 
    
         
            +
                def dump_env(env)
         
     | 
| 
      
 302 
     | 
    
         
            +
                  headers = {'Content-Type' => 'text/plain'}
         
     | 
| 
      
 303 
     | 
    
         
            +
                  body = "" 
         
     | 
| 
      
 304 
     | 
    
         
            +
                  env.each do |k,v|
         
     | 
| 
      
 305 
     | 
    
         
            +
                    body << "#{k}: #{v}\n"
         
     | 
| 
      
 306 
     | 
    
         
            +
                  end
         
     | 
| 
      
 307 
     | 
    
         
            +
                  [200, headers, [body]]
         
     | 
| 
      
 308 
     | 
    
         
            +
                end
         
     | 
| 
      
 309 
     | 
    
         
            +
             
     | 
| 
      
 310 
     | 
    
         
            +
                def help(category = nil)
         
     | 
| 
      
 311 
     | 
    
         
            +
                  headers = {'Content-Type' => 'text/plain'}
         
     | 
| 
      
 312 
     | 
    
         
            +
                  body = "Append the following to your query string:
         
     | 
| 
      
 313 
     | 
    
         
            +
             
     | 
| 
      
 314 
     | 
    
         
            +
              pp=help : display this screen
         
     | 
| 
      
 315 
     | 
    
         
            +
              pp=env : display the rack environment
         
     | 
| 
      
 316 
     | 
    
         
            +
              pp=skip : skip mini profiler for this request
         
     | 
| 
      
 317 
     | 
    
         
            +
              pp=no-backtrace : don't collect stack traces from all the SQL executed
         
     | 
| 
      
 318 
     | 
    
         
            +
              pp=full-backtrace : enable full backtrace for SQL executed
         
     | 
| 
      
 319 
     | 
    
         
            +
              pp=sample : sample stack traces and return a report isolating heavy usage (requires the stacktrace gem)
         
     | 
| 
      
 320 
     | 
    
         
            +
            "
         
     | 
| 
      
 321 
     | 
    
         
            +
                  if (category == :stacktrace)
         
     | 
| 
      
 322 
     | 
    
         
            +
                    body = "pp=stacktrace requires the stacktrace gem - add gem 'stacktrace' to your Gemfile"
         
     | 
| 
      
 323 
     | 
    
         
            +
                  end
         
     | 
| 
      
 324 
     | 
    
         
            +
                
         
     | 
| 
      
 325 
     | 
    
         
            +
                  [200, headers, [body]]
         
     | 
| 
      
 326 
     | 
    
         
            +
                end
         
     | 
| 
      
 327 
     | 
    
         
            +
             
     | 
| 
      
 328 
     | 
    
         
            +
                def analyze(traces, page_struct)
         
     | 
| 
      
 329 
     | 
    
         
            +
                  headers = {'Content-Type' => 'text/plain'}
         
     | 
| 
      
 330 
     | 
    
         
            +
                  body = "Collected: #{traces.count} stack traces. Duration(ms): #{page_struct.duration_ms}"
         
     | 
| 
      
 331 
     | 
    
         
            +
             
     | 
| 
      
 332 
     | 
    
         
            +
                  seen = {}
         
     | 
| 
      
 333 
     | 
    
         
            +
                  fulldump = ""
         
     | 
| 
      
 334 
     | 
    
         
            +
                  traces.each do |trace| 
         
     | 
| 
      
 335 
     | 
    
         
            +
                    fulldump << "\n\n"
         
     | 
| 
      
 336 
     | 
    
         
            +
                    distinct = {}
         
     | 
| 
      
 337 
     | 
    
         
            +
                    trace.each do |frame|
         
     | 
| 
      
 338 
     | 
    
         
            +
                      name = "#{frame.klass} #{frame.method}"
         
     | 
| 
      
 339 
     | 
    
         
            +
                      unless distinct[name]
         
     | 
| 
      
 340 
     | 
    
         
            +
                        distinct[name] = true
         
     | 
| 
      
 341 
     | 
    
         
            +
                        seen[name] ||= 0
         
     | 
| 
      
 342 
     | 
    
         
            +
                        seen[name] += 1
         
     | 
| 
      
 343 
     | 
    
         
            +
                      end
         
     | 
| 
      
 344 
     | 
    
         
            +
                      fulldump << name << "\n"
         
     | 
| 
      
 345 
     | 
    
         
            +
                    end
         
     | 
| 
      
 346 
     | 
    
         
            +
                  end
         
     | 
| 
      
 347 
     | 
    
         
            +
             
     | 
| 
      
 348 
     | 
    
         
            +
                  body << "\n\nStack Trace Analysis\n"
         
     | 
| 
      
 349 
     | 
    
         
            +
                  seen.to_a.sort{|x,y| y[1] <=> x[1]}.each do |name, count|
         
     | 
| 
      
 350 
     | 
    
         
            +
                    if count > traces.count / 10
         
     | 
| 
      
 351 
     | 
    
         
            +
                      body << "#{name} x #{count}\n"
         
     | 
| 
      
 352 
     | 
    
         
            +
                    end
         
     | 
| 
      
 353 
     | 
    
         
            +
                  end
         
     | 
| 
      
 354 
     | 
    
         
            +
                  
         
     | 
| 
      
 355 
     | 
    
         
            +
                  body << "\n\n\nRaw traces \n"
         
     | 
| 
      
 356 
     | 
    
         
            +
                  body << fulldump
         
     | 
| 
      
 357 
     | 
    
         
            +
             
     | 
| 
      
 358 
     | 
    
         
            +
                  [200, headers, [body]]
         
     | 
| 
      
 359 
     | 
    
         
            +
                end
         
     | 
| 
      
 360 
     | 
    
         
            +
             
     | 
| 
      
 361 
     | 
    
         
            +
                def ids_json(env)
         
     | 
| 
      
 362 
     | 
    
         
            +
                  ids = [current.page_struct["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])
         
     | 
| 
      
 363 
     | 
    
         
            +
                  ::JSON.generate(ids.uniq)
         
     | 
| 
      
 364 
     | 
    
         
            +
                end
         
     | 
| 
      
 365 
     | 
    
         
            +
             
     | 
| 
      
 366 
     | 
    
         
            +
            		# get_profile_script returns script to be injected inside current html page
         
     | 
| 
      
 367 
     | 
    
         
            +
            		# By default, profile_script is appended to the end of all html requests automatically.
         
     | 
| 
      
 368 
     | 
    
         
            +
            		# Calling get_profile_script cancels automatic append for the current page
         
     | 
| 
      
 369 
     | 
    
         
            +
            		# Use it when:
         
     | 
| 
      
 370 
     | 
    
         
            +
            		# * you have disabled auto append behaviour throught :auto_inject => false flag
         
     | 
| 
      
 371 
     | 
    
         
            +
            		# * you do not want script to be automatically appended for the current page. You can also call cancel_auto_inject
         
     | 
| 
      
 372 
     | 
    
         
            +
            		def get_profile_script(env)
         
     | 
| 
      
 373 
     | 
    
         
            +
            			ids = ids_json(env)
         
     | 
| 
      
 374 
     | 
    
         
            +
            			path = @config.base_url_path
         
     | 
| 
      
 375 
     | 
    
         
            +
            			version = MiniProfiler::VERSION
         
     | 
| 
      
 376 
     | 
    
         
            +
            			position = @config.position
         
     | 
| 
      
 377 
     | 
    
         
            +
            			showTrivial = false
         
     | 
| 
      
 378 
     | 
    
         
            +
            			showChildren = false
         
     | 
| 
      
 379 
     | 
    
         
            +
            			maxTracesToShow = 10
         
     | 
| 
      
 380 
     | 
    
         
            +
            			showControls = false
         
     | 
| 
      
 381 
     | 
    
         
            +
            			currentId = current.page_struct["Id"]
         
     | 
| 
      
 382 
     | 
    
         
            +
            			authorized = true
         
     | 
| 
      
 383 
     | 
    
         
            +
            			useExistingjQuery = @config.use_existing_jquery
         
     | 
| 
      
 384 
     | 
    
         
            +
            			# TODO : cache this snippet 
         
     | 
| 
      
 385 
     | 
    
         
            +
            			script = IO.read(::File.expand_path('../html/profile_handler.js', ::File.dirname(__FILE__)))
         
     | 
| 
      
 386 
     | 
    
         
            +
            			# replace the variables
         
     | 
| 
      
 387 
     | 
    
         
            +
            			[:ids, :path, :version, :position, :showTrivial, :showChildren, :maxTracesToShow, :showControls, :currentId, :authorized, :useExistingjQuery].each do |v|
         
     | 
| 
      
 388 
     | 
    
         
            +
            				regex = Regexp.new("\\{#{v.to_s}\\}")
         
     | 
| 
      
 389 
     | 
    
         
            +
            				script.gsub!(regex, eval(v.to_s).to_s)
         
     | 
| 
      
 390 
     | 
    
         
            +
            			end
         
     | 
| 
      
 391 
     | 
    
         
            +
            			# replace the '{{' and '}}''
         
     | 
| 
      
 392 
     | 
    
         
            +
            			script.gsub!(/\{\{/, '{').gsub!(/\}\}/, '}')
         
     | 
| 
      
 393 
     | 
    
         
            +
            			current.inject_js = false
         
     | 
| 
      
 394 
     | 
    
         
            +
            			script
         
     | 
| 
      
 395 
     | 
    
         
            +
            		end
         
     | 
| 
      
 396 
     | 
    
         
            +
             
     | 
| 
      
 397 
     | 
    
         
            +
            		# cancels automatic injection of profile script for the current page
         
     | 
| 
      
 398 
     | 
    
         
            +
            		def cancel_auto_inject(env)
         
     | 
| 
      
 399 
     | 
    
         
            +
            		  current.inject_js = false
         
     | 
| 
      
 400 
     | 
    
         
            +
            		end
         
     | 
| 
      
 401 
     | 
    
         
            +
             
     | 
| 
      
 402 
     | 
    
         
            +
            	end
         
     | 
| 
      
 403 
     | 
    
         
            +
             
     | 
| 
      
 404 
     | 
    
         
            +
            end
         
     | 
| 
      
 405 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,73 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Rack
         
     | 
| 
      
 2 
     | 
    
         
            +
              class MiniProfiler
         
     | 
| 
      
 3 
     | 
    
         
            +
                module ProfilingMethods 
         
     | 
| 
      
 4 
     | 
    
         
            +
                 
         
     | 
| 
      
 5 
     | 
    
         
            +
            		  def record_sql(query, elapsed_ms)
         
     | 
| 
      
 6 
     | 
    
         
            +
                    c = current
         
     | 
| 
      
 7 
     | 
    
         
            +
                    return unless c
         
     | 
| 
      
 8 
     | 
    
         
            +
            			  c.current_timer.add_sql(query, elapsed_ms, c.page_struct, c.skip_backtrace, c.full_backtrace) if (c && c.current_timer)
         
     | 
| 
      
 9 
     | 
    
         
            +
            		  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  # perform a profiling step on given block
         
     | 
| 
      
 12 
     | 
    
         
            +
                  def step(name)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    if current
         
     | 
| 
      
 14 
     | 
    
         
            +
                      parent_timer = current.current_timer
         
     | 
| 
      
 15 
     | 
    
         
            +
                      result = nil
         
     | 
| 
      
 16 
     | 
    
         
            +
                      current.current_timer = current_timer = current.current_timer.add_child(name) 
         
     | 
| 
      
 17 
     | 
    
         
            +
                      begin 
         
     | 
| 
      
 18 
     | 
    
         
            +
                        result = yield if block_given?
         
     | 
| 
      
 19 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 20 
     | 
    
         
            +
                        current_timer.record_time
         
     | 
| 
      
 21 
     | 
    
         
            +
                        current.current_timer = parent_timer
         
     | 
| 
      
 22 
     | 
    
         
            +
                      end
         
     | 
| 
      
 23 
     | 
    
         
            +
                      result
         
     | 
| 
      
 24 
     | 
    
         
            +
                    else
         
     | 
| 
      
 25 
     | 
    
         
            +
                      yield if block_given?
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def unprofile_method(klass, method)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    with_profiling = (method.to_s + "_with_mini_profiler").intern
         
     | 
| 
      
 31 
     | 
    
         
            +
                    without_profiling = (method.to_s + "_without_mini_profiler").intern
         
     | 
| 
      
 32 
     | 
    
         
            +
                    
         
     | 
| 
      
 33 
     | 
    
         
            +
                    if klass.send :method_defined?, with_profiling
         
     | 
| 
      
 34 
     | 
    
         
            +
                      klass.send :alias_method, method, without_profiling
         
     | 
| 
      
 35 
     | 
    
         
            +
                      klass.send :remove_method, with_profiling
         
     | 
| 
      
 36 
     | 
    
         
            +
                      klass.send :remove_method, without_profiling
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def profile_method(klass, method, &blk)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    default_name = klass.to_s + " " + method.to_s
         
     | 
| 
      
 42 
     | 
    
         
            +
                    with_profiling = (method.to_s + "_with_mini_profiler").intern
         
     | 
| 
      
 43 
     | 
    
         
            +
                    without_profiling = (method.to_s + "_without_mini_profiler").intern
         
     | 
| 
      
 44 
     | 
    
         
            +
                    
         
     | 
| 
      
 45 
     | 
    
         
            +
                    if klass.send :method_defined?, with_profiling
         
     | 
| 
      
 46 
     | 
    
         
            +
                      return # dont double profile
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
                      
         
     | 
| 
      
 49 
     | 
    
         
            +
                    klass.send :alias_method, without_profiling, method
         
     | 
| 
      
 50 
     | 
    
         
            +
                    klass.send :define_method, with_profiling do |*args, &orig|
         
     | 
| 
      
 51 
     | 
    
         
            +
                      return self.send without_profiling, *args, &orig unless Rack::MiniProfiler.current
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                      name = default_name 
         
     | 
| 
      
 54 
     | 
    
         
            +
                      name = blk.bind(self).call(*args) if blk
         
     | 
| 
      
 55 
     | 
    
         
            +
                      
         
     | 
| 
      
 56 
     | 
    
         
            +
                      parent_timer = Rack::MiniProfiler.current.current_timer
         
     | 
| 
      
 57 
     | 
    
         
            +
                      page_struct = Rack::MiniProfiler.current.page_struct
         
     | 
| 
      
 58 
     | 
    
         
            +
                      result = nil
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                      Rack::MiniProfiler.current.current_timer = current_timer = parent_timer.add_child(name) 
         
     | 
| 
      
 61 
     | 
    
         
            +
                      begin 
         
     | 
| 
      
 62 
     | 
    
         
            +
                        result = self.send without_profiling, *args, &orig
         
     | 
| 
      
 63 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 64 
     | 
    
         
            +
                        current_timer.record_time
         
     | 
| 
      
 65 
     | 
    
         
            +
                        Rack::MiniProfiler.current.current_timer = parent_timer
         
     | 
| 
      
 66 
     | 
    
         
            +
                      end 
         
     | 
| 
      
 67 
     | 
    
         
            +
                      result 
         
     | 
| 
      
 68 
     | 
    
         
            +
                    end
         
     | 
| 
      
 69 
     | 
    
         
            +
                    klass.send :alias_method, method, with_profiling
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
              end
         
     | 
| 
      
 73 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,96 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'mini_profiler/timer_struct'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Rack
         
     | 
| 
      
 4 
     | 
    
         
            +
              class MiniProfiler
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                class RequestTimerStruct < TimerStruct
         
     | 
| 
      
 7 
     | 
    
         
            +
                  
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def self.createRoot(name, page)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    rt = RequestTimerStruct.new(name, page, nil)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    rt["IsRoot"]= true
         
     | 
| 
      
 11 
     | 
    
         
            +
                    rt
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  attr_accessor :children_duration
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def initialize(name, page, parent)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    super("Id" => MiniProfiler.generate_id,
         
     | 
| 
      
 18 
     | 
    
         
            +
                          "Name" => name,
         
     | 
| 
      
 19 
     | 
    
         
            +
                          "DurationMilliseconds" => 0,
         
     | 
| 
      
 20 
     | 
    
         
            +
                          "DurationWithoutChildrenMilliseconds"=> 0,
         
     | 
| 
      
 21 
     | 
    
         
            +
                          "StartMilliseconds" => (Time.now.to_f * 1000).to_i - page['Started'],
         
     | 
| 
      
 22 
     | 
    
         
            +
                          "ParentTimingId" => nil,
         
     | 
| 
      
 23 
     | 
    
         
            +
                          "Children" => [],
         
     | 
| 
      
 24 
     | 
    
         
            +
                          "HasChildren"=> false,
         
     | 
| 
      
 25 
     | 
    
         
            +
                          "KeyValues" => nil,
         
     | 
| 
      
 26 
     | 
    
         
            +
                          "HasSqlTimings"=> false,
         
     | 
| 
      
 27 
     | 
    
         
            +
                          "HasDuplicateSqlTimings"=> false,
         
     | 
| 
      
 28 
     | 
    
         
            +
                          "TrivialDurationThresholdMilliseconds" => 2,
         
     | 
| 
      
 29 
     | 
    
         
            +
                          "SqlTimings" => [],
         
     | 
| 
      
 30 
     | 
    
         
            +
                          "SqlTimingsDurationMilliseconds"=> 0,
         
     | 
| 
      
 31 
     | 
    
         
            +
                          "IsTrivial"=> false,
         
     | 
| 
      
 32 
     | 
    
         
            +
                          "IsRoot"=> false,
         
     | 
| 
      
 33 
     | 
    
         
            +
                          "Depth"=> parent ? parent.depth + 1 : 0,
         
     | 
| 
      
 34 
     | 
    
         
            +
                          "ExecutedReaders"=> 0,
         
     | 
| 
      
 35 
     | 
    
         
            +
                          "ExecutedScalars"=> 0,
         
     | 
| 
      
 36 
     | 
    
         
            +
                          "ExecutedNonQueries"=> 0)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    @children_duration = 0
         
     | 
| 
      
 38 
     | 
    
         
            +
                    @start = Time.now
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @parent = parent
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @page = page
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def duration_ms
         
     | 
| 
      
 44 
     | 
    
         
            +
                    self['DurationMilliseconds']
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  def start_ms
         
     | 
| 
      
 48 
     | 
    
         
            +
                    self['StartMilliseconds']
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def start
         
     | 
| 
      
 52 
     | 
    
         
            +
                    @start
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def depth
         
     | 
| 
      
 56 
     | 
    
         
            +
                    self['Depth']
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  def children
         
     | 
| 
      
 60 
     | 
    
         
            +
                    self['Children']
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def add_child(name)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    request_timer =  RequestTimerStruct.new(name, @page, self)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    self['Children'].push(request_timer)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    self['HasChildren'] = true
         
     | 
| 
      
 67 
     | 
    
         
            +
                    request_timer['ParentTimingId'] = self['Id']
         
     | 
| 
      
 68 
     | 
    
         
            +
                    request_timer['Depth'] = self['Depth'] + 1
         
     | 
| 
      
 69 
     | 
    
         
            +
                    request_timer
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  def add_sql(query, elapsed_ms, page, skip_backtrace = false, full_backtrace = false)
         
     | 
| 
      
 73 
     | 
    
         
            +
                    timer = SqlTimerStruct.new(query, elapsed_ms, page, self , skip_backtrace, full_backtrace)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    timer['ParentTimingId'] = self['Id']
         
     | 
| 
      
 75 
     | 
    
         
            +
                    self['SqlTimings'].push(timer)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    self['HasSqlTimings'] = true
         
     | 
| 
      
 77 
     | 
    
         
            +
                    self['SqlTimingsDurationMilliseconds'] += elapsed_ms
         
     | 
| 
      
 78 
     | 
    
         
            +
                    page['DurationMillisecondsInSql'] += elapsed_ms        
         
     | 
| 
      
 79 
     | 
    
         
            +
                    timer
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def record_time(milliseconds = nil)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    milliseconds ||= (Time.now - @start) * 1000
         
     | 
| 
      
 84 
     | 
    
         
            +
                    self['DurationMilliseconds'] = milliseconds
         
     | 
| 
      
 85 
     | 
    
         
            +
                    self['IsTrivial'] = true if milliseconds < self["TrivialDurationThresholdMilliseconds"]
         
     | 
| 
      
 86 
     | 
    
         
            +
                    self['DurationWithoutChildrenMilliseconds'] = milliseconds - @children_duration
         
     | 
| 
      
 87 
     | 
    
         
            +
                    
         
     | 
| 
      
 88 
     | 
    
         
            +
                    if @parent
         
     | 
| 
      
 89 
     | 
    
         
            +
                      @parent.children_duration += milliseconds
         
     | 
| 
      
 90 
     | 
    
         
            +
                    end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  end     
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
              end
         
     | 
| 
      
 95 
     | 
    
         
            +
              
         
     | 
| 
      
 96 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,48 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'mini_profiler/timer_struct'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Rack
         
     | 
| 
      
 4 
     | 
    
         
            +
              class MiniProfiler
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # Timing system for a SQL query
         
     | 
| 
      
 7 
     | 
    
         
            +
                class SqlTimerStruct < TimerStruct
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def initialize(query, duration_ms, page, parent, skip_backtrace = false, full_backtrace = false)
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    stack_trace = nil 
         
     | 
| 
      
 11 
     | 
    
         
            +
                    unless skip_backtrace 
         
     | 
| 
      
 12 
     | 
    
         
            +
                      # Allow us to filter the stack trace
         
     | 
| 
      
 13 
     | 
    
         
            +
                      stack_trace = ""
         
     | 
| 
      
 14 
     | 
    
         
            +
                       # Clean up the stack trace if there are options to do so
         
     | 
| 
      
 15 
     | 
    
         
            +
                      Kernel.caller.each do |ln|
         
     | 
| 
      
 16 
     | 
    
         
            +
                        ln.gsub!(Rack::MiniProfiler.config.backtrace_remove, '') if Rack::MiniProfiler.config.backtrace_remove and !full_backtrace
         
     | 
| 
      
 17 
     | 
    
         
            +
                        if full_backtrace or Rack::MiniProfiler.config.backtrace_filter.nil? or ln =~ Rack::MiniProfiler.config.backtrace_filter
         
     | 
| 
      
 18 
     | 
    
         
            +
                          stack_trace << ln << "\n" 
         
     | 
| 
      
 19 
     | 
    
         
            +
                        end
         
     | 
| 
      
 20 
     | 
    
         
            +
                      end
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    @parent = parent
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @page = page
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    super("ExecuteType" => 3, # TODO
         
     | 
| 
      
 27 
     | 
    
         
            +
                          "FormattedCommandString" => query,
         
     | 
| 
      
 28 
     | 
    
         
            +
                          "StackTraceSnippet" => stack_trace, 
         
     | 
| 
      
 29 
     | 
    
         
            +
                          "StartMilliseconds" => ((Time.now.to_f * 1000).to_i - page['Started']) - duration_ms,
         
     | 
| 
      
 30 
     | 
    
         
            +
                          "DurationMilliseconds" => duration_ms,
         
     | 
| 
      
 31 
     | 
    
         
            +
                          "FirstFetchDurationMilliseconds" => duration_ms,
         
     | 
| 
      
 32 
     | 
    
         
            +
                          "Parameters" => nil,
         
     | 
| 
      
 33 
     | 
    
         
            +
                          "ParentTimingId" => nil,
         
     | 
| 
      
 34 
     | 
    
         
            +
                          "IsDuplicate" => false)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  def report_reader_duration(elapsed_ms)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    return if @reported
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @reported = true
         
     | 
| 
      
 40 
     | 
    
         
            +
                    self["DurationMilliseconds"] += elapsed_ms
         
     | 
| 
      
 41 
     | 
    
         
            +
                    @parent["SqlTimingsDurationMilliseconds"] += elapsed_ms
         
     | 
| 
      
 42 
     | 
    
         
            +
                    @page["DurationMillisecondsInSql"] += elapsed_ms
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Rack
         
     | 
| 
      
 2 
     | 
    
         
            +
              class MiniProfiler
         
     | 
| 
      
 3 
     | 
    
         
            +
                class AbstractStore
         
     | 
| 
      
 4 
     | 
    
         
            +
                  
         
     | 
| 
      
 5 
     | 
    
         
            +
                  def save(page_struct)
         
     | 
| 
      
 6 
     | 
    
         
            +
                    raise NotImplementedError.new("save is not implemented")
         
     | 
| 
      
 7 
     | 
    
         
            +
                  end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  def load(id)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    raise NotImplementedError.new("load is not implemented")
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def set_unviewed(user, id)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    raise NotImplementedError.new("set_unviewed is not implemented")
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def set_viewed(user, id)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    raise NotImplementedError.new("set_viewed is not implemented")
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def get_unviewed_ids(user)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    raise NotImplementedError.new("get_unviewed_ids is not implemented")
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     |