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.
- 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 +13 -1
- data/README +25 -17
- data/Rakefile +10 -2
- data/VERSION-GEN +1 -1
- data/ext/mwrap/check.h +23 -0
- data/ext/mwrap/dlmalloc_c.h +6294 -0
- data/ext/mwrap/extconf.rb +3 -7
- data/ext/mwrap/gcc.h +13 -0
- data/ext/mwrap/httpd.h +1367 -0
- data/ext/mwrap/mwrap.c +44 -1151
- 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/version.rb +1 -1
- data/lib/mwrap_rack.rb +14 -58
- data/mwrap.gemspec +10 -3
- data/t/httpd.t +191 -0
- data/t/test_common.perl +54 -0
- data/test/test_mwrap.rb +34 -50
- metadata +21 -7
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,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-
|
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;
|
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,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-
|
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:
|
99
|
+
version: 1.3.1
|
86
100
|
requirements: []
|
87
|
-
rubygems_version: 3.
|
101
|
+
rubygems_version: 3.4.1
|
88
102
|
signing_key:
|
89
103
|
specification_version: 4
|
90
104
|
summary: LD_PRELOAD malloc wrapper for Ruby
|