From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-3.9 required=3.0 tests=ALL_TRUSTED,AWL,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.1 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 0BC05208FF for ; Mon, 16 Jul 2018 21:19:39 +0000 (UTC) From: Eric Wong To: mwrap-public@80x24.org Subject: [PATCH 19/19] mwrap_rack: Rack app to track live allocations Date: Mon, 16 Jul 2018 21:19:33 +0000 Message-Id: <20180716211933.5835-20-e@80x24.org> In-Reply-To: <20180716211933.5835-1-e@80x24.org> References: <20180716211933.5835-1-e@80x24.org> List-Id: 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 -- EW