From c432e3ad30aa247dbac8575af87b0c594365d3fd Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 16 Jul 2018 21:05:45 +0000 Subject: mwrap_rack: Rack app to track live allocations Might as well be able to see and inspect what allocations are alive when developing a Rack application. Demo is available at https://80x24.org/MWRAP/each/2000 (note: "MWRAP" is capitalized) This is also running "repobrowse", which will eventually replace cgit on 80x24.org, but is NOT running the main HTTPS termination at https://80x24.org/ Note: this demo machine is 32-bit, so yes, it will overflow --- lib/mwrap_rack.rb | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 lib/mwrap_rack.rb diff --git a/lib/mwrap_rack.rb b/lib/mwrap_rack.rb new file mode 100644 index 0000000..ef3872b --- /dev/null +++ b/lib/mwrap_rack.rb @@ -0,0 +1,105 @@ +# Copyright (C) 2018 all contributors +# License: GPL-2.0+ +# frozen_string_literal: true +require 'mwrap' +require 'rack' +require 'cgi' + +# Usage: mwrap rackup ... +class MwrapRack + module HtmlResponse + def response + [ 200, { + 'Expires' => 'Fri, 01 Jan 1980 00:00:00 GMT', + 'Pragma' => 'no-cache', + 'Cache-Control' => 'no-cache, max-age=0, must-revalidate', + 'Content-Type' => 'text/html; charset=UTF-8', + }, self ] + end + end + + class Each < Struct.new(:script_name, :min, :sort) + include HtmlResponse + HEADER = '' + %w(total allocations frees mean_life max_life + location).join('') + '' + FIELDS = %w(total allocations frees mean_life max_life location) + def each + Mwrap.quiet do + t = -"Mwrap.each(#{min})" + sn = script_name + all = [] + f = FIELDS.dup + sc = FIELDS.index(sort || 'total') || 0 + f[sc] = -"#{f[sc]}" + f.map! do |hdr| + if hdr.start_with?('') + hdr + else + -%Q(#{hdr}) + end + end + Mwrap.each(min) do |loc, total, allocations, frees, age_sum, max_life| + mean_life = frees == 0 ? Float::INFINITY : age_sum/frees.to_f + all << [total,allocations,frees,mean_life,max_life,loc] + end + all.sort_by! { |cols| -cols[sc] } + + yield(-"#{t}" \ + "

#{t}

\n" \ + "

Current generation: #{GC.count}

\n\n" \ + "\n") + all.each do |cols| + loc = cols.pop + cols[3] = sprintf('%0.3f', cols[3]) # mean_life + href = -(+"#{sn}/at/#{CGI.escape(loc)}").encode!(xml: :attr) + yield(%Q(\n)) + cols.clear + end.clear + yield "
#{f.join('')}
#{cols.join('')}#{-loc.encode(xml: :text)}
\n" + end + end + end + + class EachAt < Struct.new(:loc) + include HtmlResponse + HEADER = 'sizegeneration' + + def each + t = loc.name.encode(xml: :text) + yield(-"#{t}" \ + "

live allocations at #{t}

" \ + "

Current generation: #{GC.count}

\n#{HEADER}") + loc.each do |size, generation| + yield("\n") + end + yield "
#{size}#{generation}
\n" + end + end + + def r404 + [404,{'Content-Type'=>'text/plain'},["Not found\n"]] + end + + def call(env) + case env['PATH_INFO'] + when %r{\A/each/(\d+)\z} + min = $1.to_i + m = env['QUERY_STRING'].match(/\bsort=(\w+)/) + Each.new(env['SCRIPT_NAME'], min, m ? m[1] : nil).response + when %r{\A/at/(.*)\z} + loc = -CGI.unescape($1) + loc = Mwrap[loc] or return r404 + EachAt.new(loc).response + when '/' + n = 2000 + u = 'https://80x24.org/mwrap/README.html' + b = -('Mwrap demo' \ + "

allocations >#{n} bytes" \ + "

#{u}\n") + [ 200, {'Content-Type'=>'text/html','Content-Length'=>-b.size.to_s},[b]] + else + r404 + end + end +end -- cgit v1.2.3-24-ge0c7