Parent

Files

Rack::MiniProfiler

Constants

VERSION

Public Class Methods

authorize_request() click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 70
def authorize_request
  Thread.current[:mp_authorized] = true
end
config() click to toggle source

So we can change the configuration if we want

# File Ruby/lib/mini_profiler/profiler.rb, line 39
def config
  @config ||= Config.default
end
counter(type, duration_ms=nil) click to toggle source

Add a custom timing. These are displayed similar to SQL/query time in columns expanding to the right.

type - String counter type. Each distinct type gets its own column. duration_ms - Duration of the call in ms. Either this or a block must be

given but not both.

When a block is given, calculate the duration by yielding to the block and keeping a record of its run time.

Returns the result of the block, or nil when no block is given.

# File Ruby/lib/mini_profiler/profiler.rb, line 93
def counter(type, duration_ms=nil)
  result = nil
  if block_given?
    start = Time.now
    result = yield
    duration_ms = (Time.now - start).to_f * 1000
  end
  return result if current.nil? || !request_authorized?
  current.current_timer.add_custom(type, duration_ms, current.page_struct)
  result
end
create_current(env={}, options={}) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 62
def create_current(env={}, options={})
  # profiling the request
  self.current = Context.new
  self.current.inject_js = config.auto_inject && (!env['HTTP_X_REQUESTED_WITH'].eql? 'XMLHttpRequest')
  self.current.page_struct = PageTimerStruct.new(env)
  self.current.current_timer = current.page_struct['Root']
end
current() click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 48
def current
  Thread.current[:mini_profiler_private]
end
current=(c) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 52
def current=(c)
  # we use TLS cause we need access to this from sql blocks and code blocks that have no access to env
  Thread.current[:mini_profiler_private]= c
end
deauthorize_request() click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 74
def deauthorize_request
  Thread.current[:mp_authorized] = nil
end
discard_results() click to toggle source

discard existing results, don't track this request

# File Ruby/lib/mini_profiler/profiler.rb, line 58
def discard_results
  self.current.discard = true if current
end
generate_id() click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 30
def generate_id
  rand(36**20).to_s(36)
end
new(app, config = nil) click to toggle source

options: :auto_inject - should script be automatically injected on every html page (not xhr)

# File Ruby/lib/mini_profiler/profiler.rb, line 109
          def initialize(app, config = nil)
MiniProfiler.config.merge!(config)
@config = MiniProfiler.config 
                  @app = app
                  @config.base_url_path << "/" unless @config.base_url_path.end_with? "/"
unless @config.storage_instance
  @config.storage_instance = @config.storage.new(@config.storage_options)
end
@storage = @config.storage_instance 
          end
request_authorized?() click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 78
def request_authorized?
  Thread.current[:mp_authorized]
end
reset_config() click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 34
def reset_config
  @config = Config.default
end
share_template() click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 43
def share_template
  return @share_template unless @share_template.nil?
  @share_template = ::File.read(::File.expand_path("../html/share.html", ::File.dirname(__FILE__)))
end

Public Instance Methods

analyze(traces, page_struct) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 454
def analyze(traces, page_struct)
  headers = {'Content-Type' => 'text/plain'}
  body = "Collected: #{traces.count} stack traces. Duration(ms): #{page_struct.duration_ms}"

  seen = {}
  fulldump = ""
  traces.each do |trace| 
    fulldump << "\n\n"
    distinct = {}
    trace.each do |frame|
      frame = "#{frame.klass}::#{frame.method}" unless String === frame
      unless distinct[frame]
        distinct[frame] = true
        seen[frame] ||= 0
        seen[frame] += 1
      end
      fulldump << frame << "\n"
    end
  end

  body << "\n\nStack Trace Analysis\n"
  seen.to_a.sort{|x,y| y[1] <=> x[1]}.each do |name, count|
    if count > traces.count / 10
      body << "#{name} x #{count}\n"
    end
  end
  
  body << "\n\n\nRaw traces \n"
  body << fulldump

  [200, headers, [body]]
end
call(env) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 194
            def call(env)

  client_settings = ClientSettings.new(env)

  status = headers = body = nil
  query_string = env['QUERY_STRING']
  path = env['PATH_INFO']

  skip_it = (@config.pre_authorize_cb && !@config.pre_authorize_cb.call(env)) ||
            (@config.skip_paths && @config.skip_paths.any?{ |p| path[0,p.length] == p}) ||
            query_string =~ /pp=skip/ 
  
  has_profiling_cookie = client_settings.has_cookie?

  if skip_it || (@config.authorization_mode == :whitelist && !has_profiling_cookie)
    status,headers,body = @app.call(env)
    if !skip_it && @config.authorization_mode == :whitelist && !has_profiling_cookie && MiniProfiler.request_authorized? 
      client_settings.write!(headers) 
    end
    return [status,headers,body]
  end

  # handle all /mini-profiler requests here
                    return serve_html(env) if path.start_with? @config.base_url_path

  has_disable_cookie = client_settings.disable_profiling?
  # manual session disable / enable
  if query_string =~ /pp=disable/ || has_disable_cookie
    skip_it = true
  end

  if query_string =~ /pp=enable/
    skip_it = false
  end

  if skip_it
    status,headers,body = @app.call(env)
    client_settings.disable_profiling = true
    client_settings.write!(headers)
    return [status,headers,body]
  else
    client_settings.disable_profiling = false
  end

  if query_string =~ /pp=profile-gc/
    # begin
      if query_string =~ /pp=profile-gc-time/
        return Rack::MiniProfiler::GCProfiler.new.profile_gc_time(@app, env)
      else
        return Rack::MiniProfiler::GCProfiler.new.profile_gc(@app, env)
      end
    # rescue => e
    #   p e
    #   e.backtrace.each do |s|
    #     puts s
    #   end
    # end
  end
  MiniProfiler.create_current(env, @config)
  MiniProfiler.deauthorize_request if @config.authorization_mode == :whitelist
  if query_string =~ /pp=normal-backtrace/
    client_settings.backtrace_level = ClientSettings::BACKTRACE_DEFAULT
  elsif query_string =~ /pp=no-backtrace/
    current.skip_backtrace = true
    client_settings.backtrace_level = ClientSettings::BACKTRACE_NONE
  elsif query_string =~ /pp=full-backtrace/ || client_settings.backtrace_full?
    current.full_backtrace = true
    client_settings.backtrace_level = ClientSettings::BACKTRACE_FULL
  elsif client_settings.backtrace_none?
    current.skip_backtrace = true
  end

  done_sampling = false
  quit_sampler = false
  backtraces = nil
  stacktrace_installed = true
  if query_string =~ /pp=sample/
    skip_frames = 0
    backtraces = []
    t = Thread.current
    
    begin 
      require 'stacktrace'
      skip_frames = stacktrace.length
    rescue LoadError
      stacktrace_installed = false
    end

    Thread.new {
      begin
        i = 10000 # for sanity never grab more than 10k samples 
        while i > 0
          break if done_sampling
          i -= 1
          if stacktrace_installed
            backtraces << t.stacktrace(0,-(1+skip_frames), StackFrame::Flags::METHOD | StackFrame::Flags::KLASS)
          else
            backtraces << t.backtrace 
          end
          sleep 0.001
        end
      ensure
        quit_sampler = true
      end
    }
  end

                    status, headers, body = nil
  start = Time.now 
  begin 

    # Strip all the caching headers so we don't get 304s back 
    #  This solves a very annoying bug where rack mini profiler never shows up
    env['HTTP_IF_MODIFIED_SINCE'] = nil
    env['HTTP_IF_NONE_MATCH'] = nil

    status,headers,body = @app.call(env)
    client_settings.write!(headers)
  ensure
    if backtraces 
      done_sampling = true
      sleep 0.001 until quit_sampler
    end
  end

  skip_it = current.discard
  if (config.authorization_mode == :whitelist && !MiniProfiler.request_authorized?)
    # this is non-obvious, don't kill the profiling cookie on errors or short requests
    # this ensures that stuff that never reaches the rails stack does not kill profiling
    if status == 200 && ((Time.now - start) > 0.1) 
      client_settings.discard_cookie!(headers)
    end
    skip_it = true
  end

  return [status,headers,body] if skip_it

  # we must do this here, otherwise current[:discard] is not being properly treated
  if query_string =~ /pp=env/
    body.close if body.respond_to? :close
    return dump_env env
  end

  if query_string =~ /pp=help/
    body.close if body.respond_to? :close
    return help(client_settings)
  end
  
  page_struct = current.page_struct
  page_struct['User'] = user(env)
                    page_struct['Root'].record_time((Time.now - start) * 1000)

  if backtraces
    body.close if body.respond_to? :close
    return analyze(backtraces, page_struct)
  end
  

  # no matter what it is, it should be unviewed, otherwise we will miss POST
  @storage.set_unviewed(page_struct['User'], page_struct['Id'])
                    @storage.save(page_struct)
                    
  # inject headers, script
                    if status == 200

    client_settings.write!(headers)
    
    # mini profiler is meddling with stuff, we can not cache cause we will get incorrect data
    # Rack::ETag has already inserted some nonesense in the chain
    headers.delete('ETag')
    headers.delete('Date')
    headers['Cache-Control'] = 'must-revalidate, private, max-age=0'

                            # inject header
    if headers.is_a? Hash
      headers['X-MiniProfiler-Ids'] = ids_json(env)
    end

                            # inject script
                            if current.inject_js                                          && headers.has_key?('Content-Type')                                          && !headers['Content-Type'].match(/text\/html/).nil? then
                                    
      response = Rack::Response.new([], status, headers)
      script = self.get_profile_script(env)
      if String === body
        response.write inject(body,script)
      else
        body.each { |fragment| response.write inject(fragment, script) }
      end
      body.close if body.respond_to? :close
      return response.finish
                            end
                    end

  client_settings.write!(headers)
                    [status, headers, body]
ensure
  # Make sure this always happens
  current = nil
            end
cancel_auto_inject(env) click to toggle source

cancels automatic injection of profile script for the current page

# File Ruby/lib/mini_profiler/profiler.rb, line 528
def cancel_auto_inject(env)
  current.inject_js = false
end
config() click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 189
def config
  @config
end
current() click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 180
def current
  MiniProfiler.current
end
current=(c) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 184
def current=(c)
  MiniProfiler.current=c
end
dump_env(env) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 424
def dump_env(env)
  headers = {'Content-Type' => 'text/plain'}
  body = "" 
  env.each do |k,v|
    body << "#{k}: #{v}\n"
  end
  [200, headers, [body]]
end
get_profile_script(env) click to toggle source

get_profile_script returns script to be injected inside current html page By default, profile_script is appended to the end of all html requests automatically. Calling get_profile_script cancels automatic append for the current page Use it when:

  • you have disabled auto append behaviour throught :auto_inject => false flag

  • you do not want script to be automatically appended for the current page. You can also call cancel_auto_inject

# File Ruby/lib/mini_profiler/profiler.rb, line 505
def get_profile_script(env)
        ids = ids_comma_separated(env)
        path = @config.base_url_path
        version = MiniProfiler::VERSION
        position = @config.position
        showTrivial = false
        showChildren = false
        maxTracesToShow = 10
        showControls = false
        currentId = current.page_struct["Id"]
        authorized = true
        # TODO : cache this snippet 
        script = IO.read(::File.expand_path('../html/profile_handler.js', ::File.dirname(__FILE__)))
        # replace the variables
        [:ids, :path, :version, :position, :showTrivial, :showChildren, :maxTracesToShow, :showControls, :currentId, :authorized].each do |v|
                regex = Regexp.new("\\{#{v.to_s}\\}")
                script.gsub!(regex, eval(v.to_s).to_s)
        end
        current.inject_js = false
        script
end
help(client_settings) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 433
    def help(client_settings)
      headers = {'Content-Type' => 'text/plain'}
      body = "Append the following to your query string:

  pp=help : display this screen
  pp=env : display the rack environment
  pp=skip : skip mini profiler for this request
  pp=no-backtrace #{"(*) " if client_settings.backtrace_none?}: don't collect stack traces from all the SQL executed (sticky, use pp=normal-backtrace to enable)
  pp=normal-backtrace #{"(*) " if client_settings.backtrace_default?}: collect stack traces from all the SQL executed and filter normally
  pp=full-backtrace #{"(*) " if client_settings.backtrace_full?}: enable full backtraces for SQL executed (use pp=normal-backtrace to disable) 
  pp=sample : sample stack traces and return a report isolating heavy usage (experimental works best with the stacktrace gem)
  pp=disable : disable profiling for this session 
  pp=enable : enable profiling for this session (if previously disabled)
  pp=profile-gc: perform gc profiling on this request, analyzes ObjectSpace generated by request (ruby 1.9.3 only)
  pp=profile-gc-time: perform built-in gc profiling on this request (ruby 1.9.3 only)
"
    
      client_settings.write!(headers)
      [200, headers, [body]]
    end
ids_comma_separated(env) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 493
def ids_comma_separated(env)
  # cap at 10 ids, otherwise there is a chance you can blow the header
  ids = [current.page_struct["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])[0..8]
  ids.join(",")
end
ids_json(env) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 487
def ids_json(env)
  # cap at 10 ids, otherwise there is a chance you can blow the header
  ids = [current.page_struct["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])[0..8]
  ::JSON.generate(ids.uniq)
end
inject(fragment, script) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 396
def inject(fragment, script)
  if fragment.match(/<\/body>/)
    # explicit </body>

    regex = /<\/body>/
    close_tag = '</body>'
  elsif fragment.match(/<\/html>/)
    # implicit </body>

    regex = /<\/html>/
    close_tag = '</html>'
  else
    # implicit </body> and </html>. Just append the script.

    return fragment + script
  end

  fragment.sub(regex) do
    # if for whatever crazy reason we dont get a utf string,
    #   just force the encoding, no utf in the mp scripts anyway
    if script.respond_to?(:encoding) && script.respond_to?(:force_encoding)
      (script + close_tag).force_encoding(fragment.encoding)
    else
      script + close_tag
    end
  end
end
serve_html(env) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 159
          def serve_html(env)
                  file_name = env['PATH_INFO'][(@config.base_url_path.length)..1000]
                  return serve_results(env) if file_name.eql?('results')
                  full_path = ::File.expand_path("../html/#{file_name}", ::File.dirname(__FILE__))
                  return [404, {}, ["Not found"]] unless ::File.exists? full_path
                  f = Rack::File.new nil
                  f.path = full_path

begin 
  f.cache_control = "max-age:86400"
  f.serving env
rescue
  # old versions of rack have a different api 
  status, headers, body = f.serving
  headers.merge! 'Cache-Control' => "max-age:86400"
  [status, headers, body]
end

          end
serve_results(env) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 124
          def serve_results(env)
                  request = Rack::Request.new(env)      
id = request['id']
                  page_struct = @storage.load(id)
unless page_struct
  @storage.set_viewed(user(env), id) 
  return [404, {}, ["Request not found: #{request['id']} - user #{user(env)}"]] 
end
                  unless page_struct['HasUserViewed']
  page_struct['ClientTimings'] = ClientTimerStruct.init_from_form_data(env, page_struct)
                          page_struct['HasUserViewed'] = true
  @storage.save(page_struct) 
  @storage.set_viewed(user(env), id) 
                  end

result_json = page_struct.to_json
# If we're an XMLHttpRequest, serve up the contents as JSON
if request.xhr?
                  [200, { 'Content-Type' => 'application/json'}, [result_json]]
else

  # Otherwise give the HTML back
  html = MiniProfiler.share_template.dup  
  html.gsub!(/\{path\}/, @config.base_url_path)      
  html.gsub!(/\{version\}/, MiniProfiler::VERSION)      
  html.gsub!(/\{json\}/, result_json)
  html.gsub!(/\{includes\}/, get_profile_script(env))
  html.gsub!(/\{name\}/, page_struct['Name'])
  html.gsub!(/\{duration\}/, "%.1f" % page_struct.duration_ms)
  
  [200, {'Content-Type' => 'text/html'}, [html]]
end

          end
user(env) click to toggle source
# File Ruby/lib/mini_profiler/profiler.rb, line 120
def user(env)
  @config.user_provider.call(env)
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.