mwrap 2.3.0 → 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.
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 a standalone Rack application which can be
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
- 'Expires' => 'Fri, 01 Jan 1980 00:00:00 GMT',
26
- 'Pragma' => 'no-cache',
27
- 'Cache-Control' => 'no-cache, max-age=0, must-revalidate',
28
- 'Content-Type' => 'text/html; charset=UTF-8',
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,{'Content-Type'=>'text/plain'},["Not found\n"]]
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 &gt;#{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, {'Content-Type'=>'text/html','Content-Length'=>-b.size.to_s},[b]]
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,6 +1,5 @@
1
1
  git_manifest = `git ls-files 2>/dev/null`.split("\n")
2
2
  git_ok = $?.success?
3
- git_manifest << 'lib/mwrap/version.rb'.freeze # generated by ./VERSION-GEN
4
3
  manifest = File.exist?('MANIFEST') ?
5
4
  File.readlines('MANIFEST').map!(&:chomp).delete_if(&:empty?) : git_manifest
6
5
  if git_ok && manifest != git_manifest
@@ -12,11 +11,19 @@ end
12
11
 
13
12
  version = `./VERSION-GEN`.chomp
14
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
15
22
 
16
23
  Gem::Specification.new do |s|
17
24
  s.name = 'mwrap'
18
25
  s.version = version
19
- s.homepage = 'https://80x24.org/mwrap/'
26
+ s.homepage = 'https://80x24.org/mwrap.git/'
20
27
  s.authors = ["mwrap hackers"]
21
28
  s.summary = 'LD_PRELOAD malloc wrapper for Ruby'
22
29
  s.executables = %w(mwrap)
@@ -31,5 +38,5 @@ source location of such calls and bytes allocated at each callsite.
31
38
 
32
39
  s.add_development_dependency('test-unit', '~> 3.0')
33
40
  s.add_development_dependency('rake-compiler', '~> 1.0')
34
- s.licenses = %w(GPL-2.0+)
41
+ s.licenses = %w(GPL-3.0+)
35
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;
@@ -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){p("HELLO_WORLD")}
132
+ -e trap(:USR1){STDOUT.syswrite("HELLO_WORLD\n")}
128
133
  -e END{Mwrap.dump}
129
- -e puts -e STDIN.read)
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: '/dev/null')
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
- while IO.select([r], nil, nil, 0.1)
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
- buf << tmp
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.wait(pid)
146
- assert_predicate $?, :success?, $?.inspect
147
- assert_equal(["\"HELLO_WORLD\"\n"], buf.split(/^/).uniq)
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 && addr.frozen? or abort 'Mwrap.each returned unfrozen address'
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: 2.3.0
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: 2022-09-03 00:00:00.000000000 Z
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,22 +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
58
62
  - VERSION-GEN
59
63
  - bin/mwrap
64
+ - ext/mwrap/check.h
65
+ - ext/mwrap/dlmalloc_c.h
60
66
  - ext/mwrap/extconf.rb
67
+ - ext/mwrap/gcc.h
68
+ - ext/mwrap/httpd.h
61
69
  - ext/mwrap/jhash.h
62
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
63
75
  - lib/mwrap/.gitignore
64
76
  - lib/mwrap/version.rb
65
77
  - lib/mwrap_rack.rb
66
78
  - mwrap.gemspec
79
+ - t/httpd.t
80
+ - t/test_common.perl
67
81
  - test/test_mwrap.rb
68
- homepage: https://80x24.org/mwrap/
82
+ homepage: https://80x24.org/mwrap.git/
69
83
  licenses:
70
- - GPL-2.0+
84
+ - GPL-3.0+
71
85
  metadata: {}
72
86
  post_install_message:
73
87
  rdoc_options: []
@@ -80,11 +94,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
80
94
  version: '0'
81
95
  required_rubygems_version: !ruby/object:Gem::Requirement
82
96
  requirements:
83
- - - ">="
97
+ - - ">"
84
98
  - !ruby/object:Gem::Version
85
- version: '0'
99
+ version: 1.3.1
86
100
  requirements: []
87
- rubygems_version: 3.0.2
101
+ rubygems_version: 3.4.1
88
102
  signing_key:
89
103
  specification_version: 4
90
104
  summary: LD_PRELOAD malloc wrapper for Ruby