mwrap 2.2.0.1.g867b → 3.0.0.pre1
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.
- checksums.yaml +4 -4
- data/COPYING +617 -282
- data/Documentation/.gitignore +2 -0
- data/Documentation/GNUmakefile +63 -0
- data/Documentation/mwrap.1 +242 -0
- data/Documentation/mwrap.pod +123 -0
- data/MANIFEST +15 -0
- data/README +25 -17
- data/Rakefile +10 -2
- data/VERSION-GEN +36 -0
- data/bin/mwrap +19 -0
- data/ext/mwrap/check.h +23 -0
- data/ext/mwrap/dlmalloc_c.h +6294 -0
- data/ext/mwrap/extconf.rb +7 -11
- data/ext/mwrap/gcc.h +13 -0
- data/ext/mwrap/httpd.h +1367 -0
- data/ext/mwrap/mwrap.c +44 -1138
- data/ext/mwrap/mwrap_core.h +1095 -0
- data/ext/mwrap/mymalloc.h +299 -0
- data/ext/mwrap/picohttpparser.h +92 -0
- data/ext/mwrap/picohttpparser_c.h +670 -0
- data/lib/mwrap/.gitignore +1 -0
- data/lib/mwrap/version.rb +1 -0
- data/lib/mwrap_rack.rb +14 -58
- data/mwrap.gemspec +15 -5
- data/t/httpd.t +191 -0
- data/t/test_common.perl +54 -0
- data/test/test_mwrap.rb +34 -50
- metadata +22 -5
    
        data/lib/mwrap_rack.rb
    CHANGED
    
    | @@ -5,9 +5,16 @@ require 'mwrap' | |
| 5 5 | 
             
            require 'rack'
         | 
| 6 6 | 
             
            require 'cgi'
         | 
| 7 7 |  | 
| 8 | 
            -
            # MwrapRack is  | 
| 8 | 
            +
            # MwrapRack is an obsolete standalone Rack application which can be
         | 
| 9 9 | 
             
            # mounted to run within your application process.
         | 
| 10 10 | 
             
            #
         | 
| 11 | 
            +
            # The embedded mwrap-httpd for Unix sockets and mwrap-rproxy for TCP
         | 
| 12 | 
            +
            # from the Perl version <https://80x24.org/mwrap-perl.git/> replaces
         | 
| 13 | 
            +
            # this in a non-obtrusive way for code which can't handle Ruby-level
         | 
| 14 | 
            +
            # threads.
         | 
| 15 | 
            +
            #
         | 
| 16 | 
            +
            # The remaining documentation remains for historical purposes:
         | 
| 17 | 
            +
            #
         | 
| 11 18 | 
             
            # Using the Rack::Builder API in config.ru, you can map it to
         | 
| 12 19 | 
             
            # the "/MWRAP/" endpoint.  As with the rest of the Mwrap API,
         | 
| 13 20 | 
             
            # your Rack server needs to be spawned with the mwrap(1)
         | 
| @@ -22,10 +29,10 @@ class MwrapRack | |
| 22 29 | 
             
              module HtmlResponse # :nodoc:
         | 
| 23 30 | 
             
                def response
         | 
| 24 31 | 
             
                  [ 200, {
         | 
| 25 | 
            -
                      ' | 
| 26 | 
            -
                      ' | 
| 27 | 
            -
                      ' | 
| 28 | 
            -
                      ' | 
| 32 | 
            +
                      'expires' => 'Fri, 01 Jan 1980 00:00:00 GMT',
         | 
| 33 | 
            +
                      'pragma' => 'no-cache',
         | 
| 34 | 
            +
                      'cache-control' => 'no-cache, max-age=0, must-revalidate',
         | 
| 35 | 
            +
                      'content-type' => 'text/html; charset=UTF-8',
         | 
| 29 36 | 
             
                    }, self ]
         | 
| 30 37 | 
             
                end
         | 
| 31 38 | 
             
              end
         | 
| @@ -89,56 +96,8 @@ class MwrapRack | |
| 89 96 | 
             
                end
         | 
| 90 97 | 
             
              end
         | 
| 91 98 |  | 
| 92 | 
            -
              class HeapPages # :nodoc:
         | 
| 93 | 
            -
                include HtmlResponse
         | 
| 94 | 
            -
                HEADER = '<tr><th>address</th><th>generation</th></tr>'
         | 
| 95 | 
            -
             | 
| 96 | 
            -
                def hpb_rows
         | 
| 97 | 
            -
                  Mwrap::HeapPageBody.stat(stat = Thread.current[:mwrap_hpb_stat] ||= {})
         | 
| 98 | 
            -
                  %i(lifespan_max lifespan_min lifespan_mean lifespan_stddev
         | 
| 99 | 
            -
                     deathspan_max deathspan_min deathspan_mean deathspan_stddev
         | 
| 100 | 
            -
                     resurrects
         | 
| 101 | 
            -
                    ).map! do |k|
         | 
| 102 | 
            -
                     "<tr><td>#{k}</td><td>#{stat[k]}</td></tr>\n"
         | 
| 103 | 
            -
                  end.join
         | 
| 104 | 
            -
                end
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                def gc_stat_rows
         | 
| 107 | 
            -
                  GC.stat(stat = Thread.current[:mwrap_gc_stat] ||= {})
         | 
| 108 | 
            -
                  %i(count heap_allocated_pages heap_eden_pages heap_tomb_pages
         | 
| 109 | 
            -
                      total_allocated_pages total_freed_pages).map do |k|
         | 
| 110 | 
            -
                     "<tr><td>GC.stat(:#{k})</td><td>#{stat[k]}</td></tr>\n"
         | 
| 111 | 
            -
                  end.join
         | 
| 112 | 
            -
                end
         | 
| 113 | 
            -
             | 
| 114 | 
            -
                GC_STAT_URL = 'https://docs.ruby-lang.org/en/trunk/GC.html#method-c-stat'
         | 
| 115 | 
            -
                GC_STAT_HELP = <<~EOM
         | 
| 116 | 
            -
                  <p>Non-Infinity lifespans can indicate fragmentation.
         | 
| 117 | 
            -
                  <p>See <a
         | 
| 118 | 
            -
                  href="#{GC_STAT_URL}">#{GC_STAT_URL}</a> for info on GC.stat values.
         | 
| 119 | 
            -
                EOM
         | 
| 120 | 
            -
             | 
| 121 | 
            -
                def each
         | 
| 122 | 
            -
                  Mwrap.quiet do
         | 
| 123 | 
            -
                    yield("<html><head><title>heap pages</title></head>" \
         | 
| 124 | 
            -
                          "<body><h1>heap pages</h1>" \
         | 
| 125 | 
            -
                          "<table><tr><th>stat</th><th>value</th></tr>\n" \
         | 
| 126 | 
            -
                          "#{hpb_rows}" \
         | 
| 127 | 
            -
                          "#{gc_stat_rows}" \
         | 
| 128 | 
            -
                          "</table>\n" \
         | 
| 129 | 
            -
                          "#{GC_STAT_HELP}" \
         | 
| 130 | 
            -
                          "<table>#{HEADER}")
         | 
| 131 | 
            -
                    Mwrap::HeapPageBody.each do |addr, generation|
         | 
| 132 | 
            -
                      addr = -sprintf('0x%x', addr)
         | 
| 133 | 
            -
                      yield(-"<tr><td>#{addr}</td><td>#{generation}</td></tr>\n")
         | 
| 134 | 
            -
                    end
         | 
| 135 | 
            -
                    yield "</table></body></html>\n"
         | 
| 136 | 
            -
                  end
         | 
| 137 | 
            -
                end
         | 
| 138 | 
            -
              end
         | 
| 139 | 
            -
             | 
| 140 99 | 
             
              def r404 # :nodoc:
         | 
| 141 | 
            -
                [404,{' | 
| 100 | 
            +
                [404,{'content-type'=>'text/plain'},["Not found\n"]]
         | 
| 142 101 | 
             
              end
         | 
| 143 102 |  | 
| 144 103 | 
             
              # The standard Rack application endpoint for MwrapRack
         | 
| @@ -152,17 +111,14 @@ class MwrapRack | |
| 152 111 | 
             
                  loc = -CGI.unescape($1)
         | 
| 153 112 | 
             
                  loc = Mwrap[loc] or return r404
         | 
| 154 113 | 
             
                  EachAt.new(loc).response
         | 
| 155 | 
            -
                when '/heap_pages'
         | 
| 156 | 
            -
                  HeapPages.new.response
         | 
| 157 114 | 
             
                when '/'
         | 
| 158 115 | 
             
                  n = 2000
         | 
| 159 116 | 
             
                  u = 'https://80x24.org/mwrap/README.html'
         | 
| 160 117 | 
             
                  b = -('<html><head><title>Mwrap demo</title></head>' \
         | 
| 161 118 | 
             
                      "<body><p><a href=\"each/#{n}\">allocations >#{n} bytes</a>" \
         | 
| 162 119 | 
             
                      "<p><a href=\"#{u}\">#{u}</a>" \
         | 
| 163 | 
            -
                      "<p><a href=\"heap_pages\">heap pages</a>" \
         | 
| 164 120 | 
             
                      "</body></html>\n")
         | 
| 165 | 
            -
                  [ 200, {' | 
| 121 | 
            +
                  [ 200, {'content-type'=>'text/html','content-length'=>-b.size.to_s},[b]]
         | 
| 166 122 | 
             
                else
         | 
| 167 123 | 
             
                  r404
         | 
| 168 124 | 
             
                end
         | 
    
        data/mwrap.gemspec
    CHANGED
    
    | @@ -1,19 +1,29 @@ | |
| 1 1 | 
             
            git_manifest = `git ls-files 2>/dev/null`.split("\n")
         | 
| 2 | 
            +
            git_ok = $?.success?
         | 
| 2 3 | 
             
            manifest = File.exist?('MANIFEST') ?
         | 
| 3 4 | 
             
              File.readlines('MANIFEST').map!(&:chomp).delete_if(&:empty?) : git_manifest
         | 
| 4 | 
            -
            if  | 
| 5 | 
            +
            if git_ok && manifest != git_manifest
         | 
| 5 6 | 
             
              tmp = "MANIFEST.#$$.tmp"
         | 
| 6 7 | 
             
              File.open(tmp, 'w') { |fp| fp.puts(git_manifest.join("\n")) }
         | 
| 7 8 | 
             
              File.rename(tmp, 'MANIFEST')
         | 
| 8 9 | 
             
              system('git add MANIFEST')
         | 
| 9 10 | 
             
            end
         | 
| 10 11 |  | 
| 11 | 
            -
             | 
| 12 | 
            +
            version = `./VERSION-GEN`.chomp
         | 
| 13 | 
            +
            $?.success? or abort './VERSION-GEN failed'
         | 
| 14 | 
            +
            manifest << 'lib/mwrap/version.rb'.freeze
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            if system(*%w(make -C Documentation man)) ||
         | 
| 17 | 
            +
               system(*%w(gmake -C Documentation man))
         | 
| 18 | 
            +
              manifest.concat(%w(Documentation/mwrap.1))
         | 
| 19 | 
            +
            else
         | 
| 20 | 
            +
              warn 'failed to build man-page(s), proceeding without them'
         | 
| 21 | 
            +
            end
         | 
| 12 22 |  | 
| 13 23 | 
             
            Gem::Specification.new do |s|
         | 
| 14 24 | 
             
              s.name = 'mwrap'
         | 
| 15 | 
            -
              s.version =  | 
| 16 | 
            -
              s.homepage = 'https://80x24.org/mwrap/'
         | 
| 25 | 
            +
              s.version = version
         | 
| 26 | 
            +
              s.homepage = 'https://80x24.org/mwrap.git/'
         | 
| 17 27 | 
             
              s.authors = ["mwrap hackers"]
         | 
| 18 28 | 
             
              s.summary = 'LD_PRELOAD malloc wrapper for Ruby'
         | 
| 19 29 | 
             
              s.executables = %w(mwrap)
         | 
| @@ -28,5 +38,5 @@ source location of such calls and bytes allocated at each callsite. | |
| 28 38 |  | 
| 29 39 | 
             
              s.add_development_dependency('test-unit', '~> 3.0')
         | 
| 30 40 | 
             
              s.add_development_dependency('rake-compiler', '~> 1.0')
         | 
| 31 | 
            -
              s.licenses = %w(GPL- | 
| 41 | 
            +
              s.licenses = %w(GPL-3.0+)
         | 
| 32 42 | 
             
            end
         | 
    
        data/t/httpd.t
    ADDED
    
    | @@ -0,0 +1,191 @@ | |
| 1 | 
            +
            #!perl -w
         | 
| 2 | 
            +
            # Copyright (C) mwrap hackers <mwrap-perl@80x24.org>
         | 
| 3 | 
            +
            # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
         | 
| 4 | 
            +
            use v5.12;
         | 
| 5 | 
            +
            use IO::Socket::UNIX;
         | 
| 6 | 
            +
            use Fcntl qw(F_GETFD F_SETFD FD_CLOEXEC);
         | 
| 7 | 
            +
            use POSIX qw(dup2 _exit mkfifo);
         | 
| 8 | 
            +
            BEGIN { require './t/test_common.perl' };
         | 
| 9 | 
            +
            my $env = { MWRAP => "socket_dir:$mwrap_tmp" };
         | 
| 10 | 
            +
            my $f1 = "$mwrap_tmp/f1";
         | 
| 11 | 
            +
            my $f2 = "$mwrap_tmp/f2";
         | 
| 12 | 
            +
            mkfifo($f1, 0600) // plan(skip_all => "mkfifo: $!");
         | 
| 13 | 
            +
            mkfifo($f2, 0600) // plan(skip_all => "mkfifo: $!");
         | 
| 14 | 
            +
            my $src = $mwrap_src ? # $mwrap_src is Perl-only, Ruby otherwise
         | 
| 15 | 
            +
            	"open my \$f1, '>', '$f1'; close \$f1; open my \$f2, '<', '$f2'" :
         | 
| 16 | 
            +
            	"File.open('$f1', 'w').close; File.open('$f2', 'r').close";
         | 
| 17 | 
            +
            my $pid = mwrap_run('httpd test', $env, '-e', $src);
         | 
| 18 | 
            +
            my $spid;
         | 
| 19 | 
            +
            my $mw_exit;
         | 
| 20 | 
            +
            my $cleanup = sub {
         | 
| 21 | 
            +
            	if (defined $spid) {
         | 
| 22 | 
            +
            		if (kill('TERM', $spid)) {
         | 
| 23 | 
            +
            			waitpid($spid, 0);
         | 
| 24 | 
            +
            			$? == 0 or warn "rproxy died with \$?=$?";
         | 
| 25 | 
            +
            		} else {
         | 
| 26 | 
            +
            			warn "kill $spid: $!";
         | 
| 27 | 
            +
            		}
         | 
| 28 | 
            +
            		undef $spid;
         | 
| 29 | 
            +
            	}
         | 
| 30 | 
            +
            	use autodie;
         | 
| 31 | 
            +
            	if (defined $pid) {
         | 
| 32 | 
            +
            		my $exit = $?;
         | 
| 33 | 
            +
            		open my $fh, '>', $f2;
         | 
| 34 | 
            +
            		close $fh;
         | 
| 35 | 
            +
            		waitpid($pid, 0);
         | 
| 36 | 
            +
            		$mw_exit = $?;
         | 
| 37 | 
            +
            		undef $pid;
         | 
| 38 | 
            +
            		diag "err: ".slurp($mwrap_err);
         | 
| 39 | 
            +
            		$? = $exit;
         | 
| 40 | 
            +
            	}
         | 
| 41 | 
            +
            };
         | 
| 42 | 
            +
            END { $cleanup->() }
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            my $sock = "$mwrap_tmp/$pid.sock";
         | 
| 45 | 
            +
            my %o = (Peer => $sock , Type => SOCK_STREAM);
         | 
| 46 | 
            +
            local $SIG{PIPE} = 'IGNORE';
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            open my $fh, '<', $f1;
         | 
| 49 | 
            +
            is(my $nil = <$fh>, undef, 'FIFO open');
         | 
| 50 | 
            +
            close $fh;
         | 
| 51 | 
            +
            ok(-S $sock, 'socket created');
         | 
| 52 | 
            +
            my $c = IO::Socket::UNIX->new(%o);
         | 
| 53 | 
            +
            ok($c, 'socket connected');
         | 
| 54 | 
            +
            is(send($c, 'GET', MSG_NOSIGNAL), 3, 'trickled 3 bytes') or diag "send: $!";
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            my $cout = "$mwrap_tmp/cout";
         | 
| 57 | 
            +
            my @curl = (qw(curl -sf --unix-socket), $sock, '-o', $cout);
         | 
| 58 | 
            +
            push @curl, '-vS' if $ENV{V};
         | 
| 59 | 
            +
            my $rc = system(@curl, "http://0/$pid/each/2000");
         | 
| 60 | 
            +
            my $curl_unix;
         | 
| 61 | 
            +
            SKIP: {
         | 
| 62 | 
            +
            	skip 'curl lacks --unix-socket support', 1 if $rc == 512;
         | 
| 63 | 
            +
            	is($rc, 0, 'curl /each');
         | 
| 64 | 
            +
            	unlink($cout);
         | 
| 65 | 
            +
            	$curl_unix = 1;
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            	$rc = system(@curl, "http://0/$pid/each/2000");
         | 
| 68 | 
            +
            	is($rc, 0, 'curl /each');
         | 
| 69 | 
            +
            	unlink($cout);
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            	$rc = system(@curl, "http://0/$pid/");
         | 
| 72 | 
            +
            	is($rc, 0, 'curl / (PID root)');
         | 
| 73 | 
            +
            	like(slurp($cout), qr/<html>/, 'root shown');
         | 
| 74 | 
            +
             | 
| 75 | 
            +
            	$rc = system(@curl, '-XPOST', "http://0/$pid/trim");
         | 
| 76 | 
            +
            	is($rc, 0, 'curl / (PID root)');
         | 
| 77 | 
            +
            	like(slurp($cout), qr/trimming/, 'trim started');
         | 
| 78 | 
            +
            	unlink($cout);
         | 
| 79 | 
            +
            };
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            {
         | 
| 82 | 
            +
            	my $req = " /$pid/each/20000 HTTP/1.0\r\n\r\n";
         | 
| 83 | 
            +
            	is(send($c, $req, MSG_NOSIGNAL), length($req),
         | 
| 84 | 
            +
            		'wrote rest of response') or diag "send: $!";
         | 
| 85 | 
            +
            	my $x = do { local $/; <$c> } or diag "readline: $!";
         | 
| 86 | 
            +
            	like($x, qr!</html>\n?\z!s, 'got complete HTML response');
         | 
| 87 | 
            +
            }
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            SKIP: {
         | 
| 90 | 
            +
            	my (@rproxy, @missing);
         | 
| 91 | 
            +
            	if (-e 'script/mwrap-rproxy') { # Perl version
         | 
| 92 | 
            +
            		@rproxy = ($^X, '-w', './blib/script/mwrap-rproxy');
         | 
| 93 | 
            +
            	} else {
         | 
| 94 | 
            +
            		my $exe = `which mwrap-rproxy`;
         | 
| 95 | 
            +
            		if ($? == 0 && defined($exe)) {
         | 
| 96 | 
            +
            			chomp($rproxy[0] = $exe);
         | 
| 97 | 
            +
            		} else {
         | 
| 98 | 
            +
            			push @missing, 'mwrap-rproxy';
         | 
| 99 | 
            +
            		}
         | 
| 100 | 
            +
            	}
         | 
| 101 | 
            +
            	for my $m (qw(Plack::Util HTTP::Tiny)) {
         | 
| 102 | 
            +
            		eval "require $m" or push(@missing, $m);
         | 
| 103 | 
            +
            	}
         | 
| 104 | 
            +
            	skip join(', ', @missing).' missing', 1 if @missing;
         | 
| 105 | 
            +
            	my $srv = IO::Socket::INET->new(LocalAddr => '127.0.0.1',
         | 
| 106 | 
            +
            				ReuseAddr => 1, Proto => 'tcp',
         | 
| 107 | 
            +
            				Type => SOCK_STREAM,
         | 
| 108 | 
            +
            				Listen => 1024);
         | 
| 109 | 
            +
            	$spid = fork;
         | 
| 110 | 
            +
            	if ($spid == 0) {
         | 
| 111 | 
            +
            		local $ENV{LISTEN_PID} = $$;
         | 
| 112 | 
            +
            		local $ENV{LISTEN_FDS} = 1;
         | 
| 113 | 
            +
            		my $fl = fcntl($srv, F_GETFD, 0);
         | 
| 114 | 
            +
            		fcntl($srv, F_SETFD, $fl &= ~FD_CLOEXEC);
         | 
| 115 | 
            +
            		if (fileno($srv) != 3) {
         | 
| 116 | 
            +
            			dup2(fileno($srv), 3) or die "dup2: $!";
         | 
| 117 | 
            +
            		}
         | 
| 118 | 
            +
            		local $ENV{PLACK_ENV} = 'deployment' if !$ENV{V};
         | 
| 119 | 
            +
            		no warnings 'exec';
         | 
| 120 | 
            +
            		exec @rproxy, "--socket-dir=$mwrap_tmp";
         | 
| 121 | 
            +
            		_exit(1);
         | 
| 122 | 
            +
            	}
         | 
| 123 | 
            +
            	my $http = HTTP::Tiny->new;
         | 
| 124 | 
            +
            	my ($h, $p) = ($srv->sockhost, $srv->sockport);
         | 
| 125 | 
            +
            	undef $srv;
         | 
| 126 | 
            +
            	my $res = $http->get("http://$h:$p/");
         | 
| 127 | 
            +
            	ok($res->{success}, 'listing success');
         | 
| 128 | 
            +
            	like($res->{content}, qr!/$pid/each/\d+!, 'got listing for each');
         | 
| 129 | 
            +
            	$res = $http->get("http://$h:$p/$pid/each/1");
         | 
| 130 | 
            +
            	ok($res->{success}, 'each/1 success');
         | 
| 131 | 
            +
            	my $t = '/at/$LOCATION link in /each/$NUM';
         | 
| 132 | 
            +
            	if ($res->{content} =~ m!href="\.\./at/([^"]+)"!) {
         | 
| 133 | 
            +
            		my $loc = $1;
         | 
| 134 | 
            +
            		ok($t);
         | 
| 135 | 
            +
            		$res = $http->get("http://$h:$p/$pid/at/$1");
         | 
| 136 | 
            +
            		ok($res->{success}, '/at/$LOCATION endpoint');
         | 
| 137 | 
            +
            		like($res->{content}, qr!\blive allocations at\b!,
         | 
| 138 | 
            +
            			'live allocations shown');
         | 
| 139 | 
            +
            	} else {
         | 
| 140 | 
            +
            		fail($t);
         | 
| 141 | 
            +
            	}
         | 
| 142 | 
            +
            	if ($ENV{INTERACTIVE}) {
         | 
| 143 | 
            +
            		diag "http://$h:$p/$pid/each/1 up for interactive testing";
         | 
| 144 | 
            +
            		diag "- press Enter when done -";
         | 
| 145 | 
            +
            		my $ok = <STDIN>;
         | 
| 146 | 
            +
            	}
         | 
| 147 | 
            +
            }
         | 
| 148 | 
            +
             | 
| 149 | 
            +
            SKIP: {
         | 
| 150 | 
            +
            	skip 'no reset w/o curl --unix-socket', 1 if !$curl_unix;
         | 
| 151 | 
            +
            	my ($sqlite_v) = (`sqlite3 --version` =~ /([\d+\.]+)/);
         | 
| 152 | 
            +
            	if ($?) {
         | 
| 153 | 
            +
            		diag 'sqlite3 missing or broken';
         | 
| 154 | 
            +
            		$sqlite_v = 0;
         | 
| 155 | 
            +
            	} else {
         | 
| 156 | 
            +
            		my @v = split(/\./, $sqlite_v);
         | 
| 157 | 
            +
            		$sqlite_v = ($v[0] << 16) | ($v[1] << 8) | $v[2];
         | 
| 158 | 
            +
            		diag 'sqlite_v='.sprintf('0x%x', $sqlite_v);
         | 
| 159 | 
            +
            	}
         | 
| 160 | 
            +
            	$rc = system(@curl, "http://0/$pid/each/100.csv");
         | 
| 161 | 
            +
            	is($rc, 0, '.csv retrieved') or skip 'CSV failed', 1;
         | 
| 162 | 
            +
            	my $db = "$mwrap_tmp/t.sqlite3";
         | 
| 163 | 
            +
             | 
| 164 | 
            +
            	if ($sqlite_v >= 0x32000) {
         | 
| 165 | 
            +
            		$rc = system(qw(sqlite3), $db,".import --csv $cout mwrap_each");
         | 
| 166 | 
            +
            		is($rc, 0, 'sqlite3 import');
         | 
| 167 | 
            +
            		my $n = `sqlite3 $db 'SELECT COUNT(*) FROM mwrap_each'`;
         | 
| 168 | 
            +
            		is($?, 0, 'sqlite3 count');
         | 
| 169 | 
            +
            		my $exp = split(/\n/, slurp($cout));
         | 
| 170 | 
            +
            		is($n + 1, $exp, 'imported all rows into sqlite');
         | 
| 171 | 
            +
            	} else {
         | 
| 172 | 
            +
            		diag "sqlite 3.32.0+ needed for `.import --csv'";
         | 
| 173 | 
            +
            	}
         | 
| 174 | 
            +
             | 
| 175 | 
            +
            	$rc = system(@curl, qw(-d x=y), "http://0/$pid/reset");
         | 
| 176 | 
            +
            	is($rc, 0, 'curl /reset');
         | 
| 177 | 
            +
            	$rc = system(@curl, qw(-HX-Mwrap-BT:10 -XPOST),
         | 
| 178 | 
            +
            			"http://0/$pid/ctl");
         | 
| 179 | 
            +
            	is($rc, 0, 'curl /ctl (X-Mwrap-BT)');
         | 
| 180 | 
            +
            	like(slurp($cout), qr/\bMWRAP=bt:10\b/, 'changed bt depth');
         | 
| 181 | 
            +
             | 
| 182 | 
            +
            	$rc = system(@curl, qw(-HX-Mwrap-BT:10 -d blah http://0/ctl));
         | 
| 183 | 
            +
            	is($rc >> 8, 22, '404 w/o PID prefix');
         | 
| 184 | 
            +
            };
         | 
| 185 | 
            +
             | 
| 186 | 
            +
             | 
| 187 | 
            +
            diag slurp($cout) if $ENV{V};
         | 
| 188 | 
            +
            $cleanup->();
         | 
| 189 | 
            +
            ok(!-e $sock, 'socket unlinked after cleanup');
         | 
| 190 | 
            +
            is($mw_exit, 0, 'perl exited with $?==0');
         | 
| 191 | 
            +
            done_testing;
         | 
    
        data/t/test_common.perl
    ADDED
    
    | @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            #!perl -w
         | 
| 2 | 
            +
            # Copyright (C) mwrap hackers <mwrap-perl@80x24.org>
         | 
| 3 | 
            +
            # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
         | 
| 4 | 
            +
            package MwrapTest;
         | 
| 5 | 
            +
            use v5.12;
         | 
| 6 | 
            +
            use parent qw(Exporter);
         | 
| 7 | 
            +
            use Test::More;
         | 
| 8 | 
            +
            use File::Temp 0.19 (); # 0.19 for ->newdir
         | 
| 9 | 
            +
            our $mwrap_src;
         | 
| 10 | 
            +
            $mwrap_src = slurp('blib/script/mwrap-perl') if -e 'script/mwrap-perl';
         | 
| 11 | 
            +
            our $mwrap_tmp = File::Temp->newdir('mwrap-XXXX', TMPDIR => 1);
         | 
| 12 | 
            +
            our $mwrap_out = "$mwrap_tmp/out";
         | 
| 13 | 
            +
            our $mwrap_err = "$mwrap_tmp/err";
         | 
| 14 | 
            +
            our @EXPORT = qw(mwrap_run slurp $mwrap_err $mwrap_out $mwrap_src $mwrap_tmp);
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            sub slurp {
         | 
| 17 | 
            +
            	open my $fh, '<', $_[0] or die "open($_[0]): $!";
         | 
| 18 | 
            +
            	local $/;
         | 
| 19 | 
            +
            	<$fh>;
         | 
| 20 | 
            +
            }
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            sub mwrap_run {
         | 
| 23 | 
            +
            	my ($msg, $env, @args) = @_;
         | 
| 24 | 
            +
            	my $pid = fork;
         | 
| 25 | 
            +
            	if ($pid == 0) {
         | 
| 26 | 
            +
            		while (my ($k, $v) = each %$env) {
         | 
| 27 | 
            +
            			$ENV{$k} = $v;
         | 
| 28 | 
            +
            		}
         | 
| 29 | 
            +
            		open STDERR, '>', $mwrap_err or die "open: $!";
         | 
| 30 | 
            +
            		open STDOUT, '>', $mwrap_out or die "open: $!";
         | 
| 31 | 
            +
            		if (defined $mwrap_src) {
         | 
| 32 | 
            +
            			unless (grep(/\A-.+\bMwrap\b/, @args)) {
         | 
| 33 | 
            +
            				unshift @args, '-MDevel::Mwrap';
         | 
| 34 | 
            +
            			}
         | 
| 35 | 
            +
            			@ARGV = ($^X, @args);
         | 
| 36 | 
            +
            			eval $mwrap_src;
         | 
| 37 | 
            +
            		} else {
         | 
| 38 | 
            +
            			my $ruby = $ENV{RUBY} // 'ruby';
         | 
| 39 | 
            +
            			exec $ruby, '-Ilib', 'bin/mwrap', $ruby, @args;
         | 
| 40 | 
            +
            		}
         | 
| 41 | 
            +
            		die "fail: $! ($@)";
         | 
| 42 | 
            +
            	}
         | 
| 43 | 
            +
            	if (defined(wantarray)) {
         | 
| 44 | 
            +
            		return $pid if !wantarray;
         | 
| 45 | 
            +
            		die "BUG: list return value not supported\n";
         | 
| 46 | 
            +
            	}
         | 
| 47 | 
            +
            	waitpid($pid, 0);
         | 
| 48 | 
            +
            	is($?, 0, $msg);
         | 
| 49 | 
            +
            	diag "err: ".slurp($mwrap_err) if $?;
         | 
| 50 | 
            +
            }
         | 
| 51 | 
            +
            package main;
         | 
| 52 | 
            +
            MwrapTest->import;
         | 
| 53 | 
            +
            Test::More->import;
         | 
| 54 | 
            +
            1;
         | 
    
        data/test/test_mwrap.rb
    CHANGED
    
    | @@ -5,6 +5,8 @@ require 'test/unit' | |
| 5 5 | 
             
            require 'mwrap'
         | 
| 6 6 | 
             
            require 'rbconfig'
         | 
| 7 7 | 
             
            require 'tempfile'
         | 
| 8 | 
            +
            require 'io/wait'
         | 
| 9 | 
            +
            require 'tmpdir'
         | 
| 8 10 |  | 
| 9 11 | 
             
            class TestMwrap < Test::Unit::TestCase
         | 
| 10 12 | 
             
              RB = "#{RbConfig::CONFIG['bindir']}/#{RbConfig::CONFIG['RUBY_INSTALL_NAME']}"
         | 
| @@ -59,13 +61,6 @@ class TestMwrap < Test::Unit::TestCase | |
| 59 61 | 
             
                  res = system(env, *cmd)
         | 
| 60 62 | 
             
                  assert res, $?.inspect
         | 
| 61 63 | 
             
                  assert_match(/\b1\d{4}\s+[1-9]\d*\s+-e:1$/, tmp.read)
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                  tmp.rewind
         | 
| 64 | 
            -
                  tmp.truncate(0)
         | 
| 65 | 
            -
                  env['MWRAP'] = "dump_path:#{tmp.path},dump_heap:5"
         | 
| 66 | 
            -
                  res = system(env, *cmd)
         | 
| 67 | 
            -
                  assert res, $?.inspect
         | 
| 68 | 
            -
                  assert_match %r{lifespan_stddev}, tmp.read
         | 
| 69 64 | 
             
                end
         | 
| 70 65 | 
             
              end
         | 
| 71 66 |  | 
| @@ -85,6 +80,15 @@ class TestMwrap < Test::Unit::TestCase | |
| 85 80 | 
             
                assert_match(/\b0x[a-f0-9]+\b/s, dump, 'dump output has addresses')
         | 
| 86 81 | 
             
              end
         | 
| 87 82 |  | 
| 83 | 
            +
              def test_spawn_non_ruby
         | 
| 84 | 
            +
                Dir.mktmpdir do |dir|
         | 
| 85 | 
            +
                  sockdir = "#{dir}/sockdir"
         | 
| 86 | 
            +
                  env = @@env.merge('MWRAP' => "socket_dir:#{sockdir}")
         | 
| 87 | 
            +
                  out = IO.popen(env, %w(ls -alR), { chdir: dir }, &:read)
         | 
| 88 | 
            +
                  assert_match(/\b\d+\.sock\b/, out)
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
             | 
| 88 92 | 
             
              def test_clear
         | 
| 89 93 | 
             
                cmd = @@cmd + %w(
         | 
| 90 94 | 
             
                  -e ("0"*10000).clear
         | 
| @@ -122,31 +126,41 @@ class TestMwrap < Test::Unit::TestCase | |
| 122 126 |  | 
| 123 127 | 
             
              # some URCU flavors use USR1, ensure the one we choose does not
         | 
| 124 128 | 
             
              def test_sigusr1_works
         | 
| 129 | 
            +
                err = Tempfile.new('dump')
         | 
| 125 130 | 
             
                cmd = @@cmd + %w(
         | 
| 126 131 | 
             
                  -e STDOUT.sync=true
         | 
| 127 | 
            -
                  -e trap(:USR1){ | 
| 132 | 
            +
                  -e trap(:USR1){STDOUT.syswrite("HELLO_WORLD\n")}
         | 
| 128 133 | 
             
                  -e END{Mwrap.dump}
         | 
| 129 | 
            -
                  -e puts | 
| 134 | 
            +
                  -e puts("HI")
         | 
| 135 | 
            +
                  -e STDIN.read)
         | 
| 130 136 | 
             
                IO.pipe do |r, w|
         | 
| 131 137 | 
             
                  IO.pipe do |r2, w2|
         | 
| 132 | 
            -
                    pid = spawn(@@env, *cmd, in: r2, out: w, err:  | 
| 138 | 
            +
                    pid = spawn(@@env, *cmd, in: r2, out: w, err: err)
         | 
| 133 139 | 
             
                    r2.close
         | 
| 134 140 | 
             
                    w.close
         | 
| 135 | 
            -
                    assert_equal "\n", r.gets
         | 
| 141 | 
            +
                    assert_equal "HI\n", r.gets, '#puts HI fired'
         | 
| 136 142 | 
             
                    buf = +''
         | 
| 137 143 | 
             
                    10.times { Process.kill(:USR1, pid) }
         | 
| 138 | 
            -
                     | 
| 144 | 
            +
                    Thread.pass # sched_yield
         | 
| 145 | 
            +
                    while r.wait_readable(0.5)
         | 
| 139 146 | 
             
                      case tmp = r.read_nonblock(1000, exception: false)
         | 
| 140 | 
            -
                      when String
         | 
| 141 | 
            -
             | 
| 147 | 
            +
                      when String; buf << tmp; break
         | 
| 148 | 
            +
                      when nil; break
         | 
| 149 | 
            +
                      else
         | 
| 150 | 
            +
                        warn "Unexpected read_nonblock result: #{tmp.inspect}"
         | 
| 142 151 | 
             
                      end
         | 
| 143 152 | 
             
                    end
         | 
| 144 | 
            -
                    w2.close
         | 
| 145 | 
            -
                    Process. | 
| 146 | 
            -
                     | 
| 147 | 
            -
                     | 
| 153 | 
            +
                    w2.close # break from STDERR.read
         | 
| 154 | 
            +
                    _, st = Process.wait2(pid)
         | 
| 155 | 
            +
                    warn "# buf=#{buf.inspect}" if $DEBUG
         | 
| 156 | 
            +
                    assert_predicate(st, :success?,
         | 
| 157 | 
            +
                      "#{st.inspect} is success buf=#{buf.inspect} "\
         | 
| 158 | 
            +
                      "err=#{err.rewind;err.read.inspect}")
         | 
| 159 | 
            +
                    assert_equal(["HELLO_WORLD\n"], buf.split(/^/).uniq)
         | 
| 148 160 | 
             
                  end
         | 
| 149 161 | 
             
                end
         | 
| 162 | 
            +
              ensure
         | 
| 163 | 
            +
                err.close! if err
         | 
| 150 164 | 
             
              end
         | 
| 151 165 |  | 
| 152 166 | 
             
              def test_reset
         | 
| @@ -257,7 +271,8 @@ class TestMwrap < Test::Unit::TestCase | |
| 257 271 | 
             
                      break
         | 
| 258 272 | 
             
                    end
         | 
| 259 273 | 
             
                  end
         | 
| 260 | 
            -
                  addr  | 
| 274 | 
            +
                  addr or abort 'Mwrap.each did not see any addresses'
         | 
| 275 | 
            +
                  addr.frozen? or abort 'Mwrap.each returned unfrozen address'
         | 
| 261 276 | 
             
                  loc = Mwrap[addr] or abort "Mwrap[#{addr}] broken"
         | 
| 262 277 | 
             
                  addr == loc.name or abort 'SourceLocation#name works on address'
         | 
| 263 278 | 
             
                  loc.name.frozen? or abort 'SourceLocation#name not frozen'
         | 
| @@ -295,35 +310,4 @@ class TestMwrap < Test::Unit::TestCase | |
| 295 310 | 
             
                    abort 'freed more than allocated'
         | 
| 296 311 | 
             
                end;
         | 
| 297 312 | 
             
              end
         | 
| 298 | 
            -
             | 
| 299 | 
            -
              def test_heap_page_body
         | 
| 300 | 
            -
                assert_separately(+"#{<<~"begin;"}\n#{<<~'end;'}")
         | 
| 301 | 
            -
                begin;
         | 
| 302 | 
            -
                  require 'mwrap'
         | 
| 303 | 
            -
                  require 'rubygems' # use up some memory
         | 
| 304 | 
            -
                  ap = GC.stat(:heap_allocated_pages)
         | 
| 305 | 
            -
                  h = {}
         | 
| 306 | 
            -
                  nr = 0
         | 
| 307 | 
            -
                  Mwrap::HeapPageBody.each do |addr, gen|
         | 
| 308 | 
            -
                    nr += 1
         | 
| 309 | 
            -
                    gen <= GC.count && gen >= 0 or abort "bad generation: #{gen}"
         | 
| 310 | 
            -
                    (0 == (addr & 16383)) or abort "addr not aligned: #{'%x' % addr}"
         | 
| 311 | 
            -
                  end
         | 
| 312 | 
            -
                  if RUBY_VERSION.to_f < 3.1 # 3.1+ uses mmap on platforms we care about
         | 
| 313 | 
            -
                    nr == ap or abort "HeapPageBody.each missed page #{nr} != #{ap}"
         | 
| 314 | 
            -
                  end
         | 
| 315 | 
            -
                  10.times { (1..20000).to_a.map(&:to_s) }
         | 
| 316 | 
            -
                  3.times { GC.start }
         | 
| 317 | 
            -
                  Mwrap::HeapPageBody.stat(h)
         | 
| 318 | 
            -
                  Integer === h[:lifespan_max] or abort 'lifespan_max not recorded'
         | 
| 319 | 
            -
                  Integer === h[:lifespan_min] or abort 'lifespan_min not recorded'
         | 
| 320 | 
            -
                  Float === h[:lifespan_mean] or abort 'lifespan_mean not recorded'
         | 
| 321 | 
            -
                  3.times { GC.start }
         | 
| 322 | 
            -
                  10.times { (1..20000).to_a.map(&:to_s) }
         | 
| 323 | 
            -
                  Mwrap::HeapPageBody.stat(h)
         | 
| 324 | 
            -
                  h[:deathspan_min] <= h[:deathspan_max] or
         | 
| 325 | 
            -
                    abort 'wrong min/max deathtime'
         | 
| 326 | 
            -
                  Float === h[:deathspan_mean] or abort 'deathspan_mean not recorded'
         | 
| 327 | 
            -
                end;
         | 
| 328 | 
            -
              end
         | 
| 329 313 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: mwrap
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version:  | 
| 4 | 
            +
              version: 3.0.0.pre1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - mwrap hackers
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2023-01-09 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: test-unit
         | 
| @@ -52,19 +52,36 @@ files: | |
| 52 52 | 
             
            - ".gitignore"
         | 
| 53 53 | 
             
            - ".olddoc.yml"
         | 
| 54 54 | 
             
            - COPYING
         | 
| 55 | 
            +
            - Documentation/.gitignore
         | 
| 56 | 
            +
            - Documentation/GNUmakefile
         | 
| 57 | 
            +
            - Documentation/mwrap.1
         | 
| 58 | 
            +
            - Documentation/mwrap.pod
         | 
| 55 59 | 
             
            - MANIFEST
         | 
| 56 60 | 
             
            - README
         | 
| 57 61 | 
             
            - Rakefile
         | 
| 62 | 
            +
            - VERSION-GEN
         | 
| 58 63 | 
             
            - bin/mwrap
         | 
| 64 | 
            +
            - ext/mwrap/check.h
         | 
| 65 | 
            +
            - ext/mwrap/dlmalloc_c.h
         | 
| 59 66 | 
             
            - ext/mwrap/extconf.rb
         | 
| 67 | 
            +
            - ext/mwrap/gcc.h
         | 
| 68 | 
            +
            - ext/mwrap/httpd.h
         | 
| 60 69 | 
             
            - ext/mwrap/jhash.h
         | 
| 61 70 | 
             
            - ext/mwrap/mwrap.c
         | 
| 71 | 
            +
            - ext/mwrap/mwrap_core.h
         | 
| 72 | 
            +
            - ext/mwrap/mymalloc.h
         | 
| 73 | 
            +
            - ext/mwrap/picohttpparser.h
         | 
| 74 | 
            +
            - ext/mwrap/picohttpparser_c.h
         | 
| 75 | 
            +
            - lib/mwrap/.gitignore
         | 
| 76 | 
            +
            - lib/mwrap/version.rb
         | 
| 62 77 | 
             
            - lib/mwrap_rack.rb
         | 
| 63 78 | 
             
            - mwrap.gemspec
         | 
| 79 | 
            +
            - t/httpd.t
         | 
| 80 | 
            +
            - t/test_common.perl
         | 
| 64 81 | 
             
            - test/test_mwrap.rb
         | 
| 65 | 
            -
            homepage: https://80x24.org/mwrap/
         | 
| 82 | 
            +
            homepage: https://80x24.org/mwrap.git/
         | 
| 66 83 | 
             
            licenses:
         | 
| 67 | 
            -
            - GPL- | 
| 84 | 
            +
            - GPL-3.0+
         | 
| 68 85 | 
             
            metadata: {}
         | 
| 69 86 | 
             
            post_install_message: 
         | 
| 70 87 | 
             
            rdoc_options: []
         | 
| @@ -81,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 81 98 | 
             
                - !ruby/object:Gem::Version
         | 
| 82 99 | 
             
                  version: 1.3.1
         | 
| 83 100 | 
             
            requirements: []
         | 
| 84 | 
            -
            rubygems_version: 3. | 
| 101 | 
            +
            rubygems_version: 3.4.1
         | 
| 85 102 | 
             
            signing_key: 
         | 
| 86 103 | 
             
            specification_version: 4
         | 
| 87 104 | 
             
            summary: LD_PRELOAD malloc wrapper for Ruby
         |