mwrap 2.2.0.1.g867b → 3.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|