mwrap 2.1.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.document +1 -0
- data/.gitignore +3 -0
- data/.olddoc.yml +3 -1
- data/MANIFEST +3 -0
- data/README +8 -6
- data/Rakefile +29 -1
- data/VERSION-GEN +36 -0
- data/bin/mwrap +20 -1
- data/ext/mwrap/extconf.rb +12 -5
- data/ext/mwrap/mwrap.c +100 -70
- data/lib/mwrap/.gitignore +1 -0
- data/lib/mwrap/version.rb +1 -0
- data/lib/mwrap_rack.rb +3 -5
- data/mwrap.gemspec +7 -4
- data/test/test_mwrap.rb +19 -12
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b967676ac9ff95d0771147fc464a06ce6a88c9a7e07c3eef97b0cf0a273ff822
|
4
|
+
data.tar.gz: f99e5f3a27adecf190e11a921a58f46c9514e28a88d83602c2421dbd8cecc90b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f3a089d3bf14258b0382d13f8d4428ffd0291d6e9af8fa33331696e5b55e5a279f0dafc30fb1018e73a199d9d3b8cbe826dd814dfd80941ae059581fa6f330c
|
7
|
+
data.tar.gz: 54a2bc06918d050e161ff0c9457ca67ebc3573a5795495d5ab57637d0bb37830e442f3073cede28d672938baaba7e7057b9d0fa8f40c64b85864add6e81e53d0
|
data/.document
CHANGED
data/.gitignore
CHANGED
data/.olddoc.yml
CHANGED
@@ -5,4 +5,6 @@ rdoc_url: https://80x24.org/mwrap/
|
|
5
5
|
ml_url: https://80x24.org/mwrap-public/
|
6
6
|
public_email: mwrap-public@80x24.org
|
7
7
|
nntp_url:
|
8
|
-
|
8
|
+
- nntps://news.public-inbox.org/inbox.comp.lang.ruby.mwrap
|
9
|
+
imap_url:
|
10
|
+
- imaps://;AUTH=ANONYMOUS@80x24.org/inbox.comp.lang.ruby.mwrap.0
|
data/MANIFEST
CHANGED
data/README
CHANGED
@@ -23,7 +23,7 @@ It does not require recompiling or rebuilding Ruby, but only
|
|
23
23
|
supports Ruby trunk (2.6.0dev+) on a few platforms:
|
24
24
|
|
25
25
|
* GNU/Linux
|
26
|
-
* FreeBSD (tested 11.1)
|
26
|
+
* FreeBSD (tested 11.1 on Ruby 2.6, currently broken with Ruby 3.x)
|
27
27
|
|
28
28
|
It may work on NetBSD, OpenBSD and DragonFly BSD.
|
29
29
|
|
@@ -67,16 +67,18 @@ first two columns to find the hottest malloc locations.
|
|
67
67
|
mwrap 2.0.0+ also supports a Rack application endpoint,
|
68
68
|
it is documented at:
|
69
69
|
|
70
|
-
|
70
|
+
https://80x24.org/mwrap/MwrapRack.html
|
71
71
|
|
72
72
|
== Known problems
|
73
73
|
|
74
74
|
* 32-bit machines are prone to overflow (WONTFIX)
|
75
75
|
|
76
|
-
==
|
76
|
+
== Public mail archives and contact info:
|
77
77
|
|
78
|
-
|
79
|
-
|
78
|
+
* https://80x24.org/mwrap-public/
|
79
|
+
* nntps://80x24.org/inbox.comp.lang.ruby.mwrap
|
80
|
+
* imaps://;AUTH=ANONYMOUS@80x24.org/inbox.comp.lang.ruby.mwrap.0
|
81
|
+
* https://80x24.org/mwrap-public/_/text/help/#pop3
|
80
82
|
|
81
83
|
No subscription will ever be required to post, but HTML mail
|
82
84
|
will be rejected:
|
@@ -88,7 +90,7 @@ will be rejected:
|
|
88
90
|
git clone https://80x24.org/mwrap.git
|
89
91
|
|
90
92
|
Send all patches and pull requests (use "git request-pull" to format) to
|
91
|
-
|
93
|
+
mwrap-public@80x24.org. We do not use centralized or proprietary messaging
|
92
94
|
systems.
|
93
95
|
|
94
96
|
== License
|
data/Rakefile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C)
|
1
|
+
# Copyright (C) mwrap hackers <mwrap-public@80x24.org>
|
2
2
|
# License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
|
3
3
|
require 'rake/testtask'
|
4
4
|
begin
|
@@ -14,3 +14,31 @@ task :default => :compile
|
|
14
14
|
|
15
15
|
c_files = File.readlines('MANIFEST').grep(%r{ext/.*\.[ch]$}).map!(&:chomp!)
|
16
16
|
task 'compile:mwrap' => c_files
|
17
|
+
|
18
|
+
olddoc = ENV['OLDDOC'] || 'olddoc'
|
19
|
+
rdoc = ENV['RDOC'] || 'rdoc'
|
20
|
+
task :rsync_docs do
|
21
|
+
require 'fileutils'
|
22
|
+
top = %w(README COPYING LATEST NEWS NEWS.atom.xml)
|
23
|
+
system("git", "set-file-times")
|
24
|
+
dest = ENV["RSYNC_DEST"] || "80x24.org:/srv/80x24/mwrap/"
|
25
|
+
FileUtils.rm_rf('doc')
|
26
|
+
sh "#{olddoc} prepare"
|
27
|
+
sh "#{rdoc} -f dark216" # dark216 requires olddoc 1.7+
|
28
|
+
File.unlink('doc/created.rid') rescue nil
|
29
|
+
File.unlink('doc/index.html') rescue nil
|
30
|
+
FileUtils.cp(top, 'doc')
|
31
|
+
sh "#{olddoc} merge"
|
32
|
+
|
33
|
+
Dir['doc/**/*'].each do |txt|
|
34
|
+
st = File.stat(txt)
|
35
|
+
if st.file?
|
36
|
+
gz = "#{txt}.gz"
|
37
|
+
tmp = "#{gz}.#$$"
|
38
|
+
sh("gzip --rsyncable -9 <#{txt} >#{tmp}")
|
39
|
+
File.utime(st.atime, st.mtime, tmp) # make nginx gzip_static happy
|
40
|
+
File.rename(tmp, gz)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
sh("rsync --chmod=Fugo=r #{ENV['RSYNC_OPT']} -av doc/ #{dest}/")
|
44
|
+
end
|
data/VERSION-GEN
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
VF=lib/mwrap/version.rb
|
3
|
+
DEF_VER=v2.3.0
|
4
|
+
VN=$(git describe HEAD 2>/dev/null)
|
5
|
+
if test $? -eq 0
|
6
|
+
then
|
7
|
+
case "$VN" in
|
8
|
+
v[0-9]*)
|
9
|
+
set -e
|
10
|
+
git update-index -q --refresh
|
11
|
+
set +e
|
12
|
+
git diff-index --quiet HEAD -- || VN="$VN-dirty"
|
13
|
+
set -e
|
14
|
+
VN=$(echo $VN | tr '-' '.')
|
15
|
+
;;
|
16
|
+
esac
|
17
|
+
fi
|
18
|
+
set -e
|
19
|
+
|
20
|
+
case $VN in
|
21
|
+
'') VN="$DEF_VER" ;;
|
22
|
+
esac
|
23
|
+
|
24
|
+
VN=$(expr "$VN" : v*'\(.*\)')
|
25
|
+
VC=unset
|
26
|
+
if test -r $VF
|
27
|
+
then
|
28
|
+
VC="$(cat $VF)"
|
29
|
+
fi
|
30
|
+
|
31
|
+
new="module Mwrap; VERSION = '$VN'.freeze; end"
|
32
|
+
if test x"$new" != x"$VC"
|
33
|
+
then
|
34
|
+
echo "$new" >$VF
|
35
|
+
fi
|
36
|
+
echo $VN
|
data/bin/mwrap
CHANGED
@@ -1,7 +1,26 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
# frozen_string_literal: true
|
3
|
-
# Copyright (C)
|
3
|
+
# Copyright (C) mwrap hackers <mwrap-public@80x24.org>
|
4
4
|
# License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
|
5
|
+
help = <<EOM
|
6
|
+
usage: mwrap COMMAND [ARGS]
|
7
|
+
see https://80x24.org/mwrap/README.html for more info
|
8
|
+
EOM
|
9
|
+
ARGV.empty? and abort help
|
10
|
+
ARGV.each do |x|
|
11
|
+
case x
|
12
|
+
when '--version', '-v'
|
13
|
+
require 'mwrap/version'
|
14
|
+
puts "mwrap #{Mwrap::VERSION} - #{RUBY_DESCRIPTION}"
|
15
|
+
exit 0
|
16
|
+
when '--help', '-h'
|
17
|
+
puts help
|
18
|
+
exit 0
|
19
|
+
else # don't intercept --version/--help intended for commands we wrap
|
20
|
+
break
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
5
24
|
require 'mwrap'
|
6
25
|
mwrap_so = $".grep(%r{/mwrap\.so\z})[0] or abort "mwrap.so not loaded"
|
7
26
|
cur = ENV['LD_PRELOAD']
|
data/ext/mwrap/extconf.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
# Copyright (C)
|
2
|
+
# Copyright (C) mwrap hackers <mwrap-public@80x24.org>
|
3
3
|
# License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
|
4
4
|
require 'mkmf'
|
5
5
|
|
@@ -11,18 +11,25 @@ have_library 'dl'
|
|
11
11
|
have_library 'c'
|
12
12
|
have_library 'execinfo' # FreeBSD
|
13
13
|
|
14
|
-
if try_link(<<
|
14
|
+
if try_link(<<EOC)
|
15
15
|
int main(void) { return __builtin_add_overflow_p(0,0,(int)1); }
|
16
|
-
|
16
|
+
EOC
|
17
17
|
$defs << '-DHAVE_BUILTIN_ADD_OVERFLOW_P'
|
18
18
|
end
|
19
19
|
|
20
|
-
if try_link(<<
|
20
|
+
if try_link(<<EOC)
|
21
21
|
int main(int a) { return __builtin_add_overflow(0,0,&a); }
|
22
|
-
|
22
|
+
EOC
|
23
23
|
$defs << '-DHAVE_BUILTIN_ADD_OVERFLOW_P'
|
24
24
|
else
|
25
25
|
abort 'missing __builtin_add_overflow'
|
26
26
|
end
|
27
27
|
|
28
|
+
begin
|
29
|
+
if n = GC::INTERNAL_CONSTANTS[:HEAP_PAGE_SIZE]
|
30
|
+
$defs << "-DHEAP_PAGE_SIZE=#{n}"
|
31
|
+
end
|
32
|
+
rescue NameError
|
33
|
+
end
|
34
|
+
|
28
35
|
create_makefile 'mwrap'
|
data/ext/mwrap/mwrap.c
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
/*
|
2
|
-
* Copyright (C)
|
2
|
+
* Copyright (C) mwrap hackers <mwrap-public@80x24.org>
|
3
3
|
* License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
|
4
4
|
*/
|
5
5
|
#define _LGPL_SOURCE /* allows URCU to inline some stuff */
|
6
|
-
#include <ruby
|
6
|
+
#include <ruby.h> /* defines HAVE_RUBY_RACTOR_H on 3.0+ */
|
7
7
|
#include <ruby/thread.h>
|
8
8
|
#include <ruby/io.h>
|
9
9
|
#include <execinfo.h>
|
@@ -22,15 +22,29 @@
|
|
22
22
|
#include <urcu/rculist.h>
|
23
23
|
#include "jhash.h"
|
24
24
|
|
25
|
+
#if __STDC_VERSION__ >= 201112
|
26
|
+
# define MWRAP_TSD _Thread_local
|
27
|
+
#elif defined(__GNUC__)
|
28
|
+
# define MWRAP_TSD __thread
|
29
|
+
#else
|
30
|
+
# error _Thread_local nor __thread supported
|
31
|
+
#endif
|
32
|
+
|
25
33
|
static ID id_uminus;
|
26
34
|
const char *rb_source_location_cstr(int *line); /* requires 2.6.0dev */
|
27
|
-
|
35
|
+
|
36
|
+
#ifdef HAVE_RUBY_RACTOR_H /* Ruby 3.0+ */
|
37
|
+
extern MWRAP_TSD void * __attribute__((weak)) ruby_current_ec;
|
38
|
+
#else /* Ruby 2.6-2.7 */
|
28
39
|
extern void * __attribute__((weak)) ruby_current_execution_context_ptr;
|
40
|
+
# define ruby_current_ec ruby_current_execution_context_ptr
|
41
|
+
#endif
|
29
42
|
extern void * __attribute__((weak)) ruby_current_vm_ptr; /* for rb_gc_count */
|
30
43
|
extern size_t __attribute__((weak)) rb_gc_count(void);
|
31
44
|
extern VALUE __attribute__((weak)) rb_cObject;
|
32
45
|
extern VALUE __attribute__((weak)) rb_eTypeError;
|
33
46
|
extern VALUE __attribute__((weak)) rb_yield(VALUE);
|
47
|
+
int __attribute__((weak)) ruby_thread_has_gvl_p(void);
|
34
48
|
|
35
49
|
static size_t total_bytes_inc, total_bytes_dec;
|
36
50
|
|
@@ -40,18 +54,16 @@ static size_t total_bytes_inc, total_bytes_dec;
|
|
40
54
|
/* match values in Ruby gc.c */
|
41
55
|
#define HEAP_PAGE_ALIGN_LOG 14
|
42
56
|
enum {
|
43
|
-
HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG)
|
57
|
+
HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG)
|
58
|
+
#ifndef HEAP_PAGE_SIZE /* Ruby 2.6-2.7 only */
|
59
|
+
,
|
44
60
|
REQUIRED_SIZE_BY_MALLOC = (sizeof(size_t) * 5),
|
45
61
|
HEAP_PAGE_SIZE = (HEAP_PAGE_ALIGN - REQUIRED_SIZE_BY_MALLOC)
|
62
|
+
#endif
|
46
63
|
};
|
47
64
|
|
48
65
|
#define IS_HEAP_PAGE_BODY ((struct src_loc *)-1)
|
49
66
|
|
50
|
-
int __attribute__((weak)) ruby_thread_has_gvl_p(void)
|
51
|
-
{
|
52
|
-
return 0;
|
53
|
-
}
|
54
|
-
|
55
67
|
#ifdef __FreeBSD__
|
56
68
|
void *__malloc(size_t);
|
57
69
|
void __free(void *);
|
@@ -75,7 +87,7 @@ static int resolving_malloc;
|
|
75
87
|
} \
|
76
88
|
} while (0)
|
77
89
|
|
78
|
-
static
|
90
|
+
static MWRAP_TSD size_t locating;
|
79
91
|
static size_t generation;
|
80
92
|
static size_t page_size;
|
81
93
|
static struct cds_lfht *totals;
|
@@ -87,9 +99,43 @@ union padded_mutex {
|
|
87
99
|
/* a round-robin pool of mutexes */
|
88
100
|
#define MUTEX_NR (1 << 6)
|
89
101
|
#define MUTEX_MASK (MUTEX_NR - 1)
|
102
|
+
#ifdef __FreeBSD__
|
103
|
+
# define STATIC_MTX_INIT_OK (0)
|
104
|
+
#else /* only tested on Linux + glibc */
|
105
|
+
# define STATIC_MTX_INIT_OK (1)
|
106
|
+
#endif
|
90
107
|
static size_t mutex_i;
|
91
108
|
static union padded_mutex mutexes[MUTEX_NR] = {
|
109
|
+
#if STATIC_MTX_INIT_OK
|
92
110
|
[0 ... (MUTEX_NR-1)].mtx = PTHREAD_MUTEX_INITIALIZER
|
111
|
+
#endif
|
112
|
+
};
|
113
|
+
|
114
|
+
#define ACC_INIT(name) { .nr=0, .min=INT64_MAX, .max=-1, .m2=0, .mean=0 }
|
115
|
+
struct acc {
|
116
|
+
uint64_t nr;
|
117
|
+
int64_t min;
|
118
|
+
int64_t max;
|
119
|
+
double m2;
|
120
|
+
double mean;
|
121
|
+
};
|
122
|
+
|
123
|
+
/* for tracking 16K-aligned heap page bodies (protected by GVL) */
|
124
|
+
struct {
|
125
|
+
pthread_mutex_t lock;
|
126
|
+
struct cds_list_head bodies;
|
127
|
+
struct cds_list_head freed;
|
128
|
+
|
129
|
+
struct acc alive;
|
130
|
+
struct acc reborn;
|
131
|
+
} hpb_stats = {
|
132
|
+
#if STATIC_MTX_INIT_OK
|
133
|
+
.lock = PTHREAD_MUTEX_INITIALIZER,
|
134
|
+
#endif
|
135
|
+
.bodies = CDS_LIST_HEAD_INIT(hpb_stats.bodies),
|
136
|
+
.freed = CDS_LIST_HEAD_INIT(hpb_stats.freed),
|
137
|
+
.alive = ACC_INIT(hpb_stats.alive),
|
138
|
+
.reborn = ACC_INIT(hpb_stats.reborn)
|
93
139
|
};
|
94
140
|
|
95
141
|
static pthread_mutex_t *mutex_assign(void)
|
@@ -108,12 +154,11 @@ __attribute__((constructor)) static void resolve_malloc(void)
|
|
108
154
|
int err;
|
109
155
|
++locating;
|
110
156
|
|
111
|
-
#ifdef __FreeBSD__
|
112
157
|
/*
|
113
158
|
* PTHREAD_MUTEX_INITIALIZER on FreeBSD means lazy initialization,
|
114
159
|
* which happens at pthread_mutex_lock, and that calls calloc
|
115
160
|
*/
|
116
|
-
{
|
161
|
+
if (!STATIC_MTX_INIT_OK) {
|
117
162
|
size_t i;
|
118
163
|
|
119
164
|
for (i = 0; i < MUTEX_NR; i++) {
|
@@ -123,24 +168,30 @@ __attribute__((constructor)) static void resolve_malloc(void)
|
|
123
168
|
_exit(1);
|
124
169
|
}
|
125
170
|
}
|
171
|
+
err = pthread_mutex_init(&hpb_stats.lock, 0);
|
172
|
+
if (err) {
|
173
|
+
fprintf(stderr, "error: %s\n", strerror(err));
|
174
|
+
_exit(1);
|
175
|
+
}
|
126
176
|
/* initialize mutexes used by urcu-bp */
|
127
177
|
rcu_read_lock();
|
128
178
|
rcu_read_unlock();
|
179
|
+
#ifndef __FreeBSD__
|
180
|
+
} else {
|
181
|
+
if (!real_malloc) {
|
182
|
+
resolving_malloc = 1;
|
183
|
+
real_malloc = dlsym(RTLD_NEXT, "malloc");
|
184
|
+
}
|
185
|
+
real_free = dlsym(RTLD_NEXT, "free");
|
186
|
+
if (!real_malloc || !real_free) {
|
187
|
+
fprintf(stderr, "missing malloc/aligned_alloc/free\n"
|
188
|
+
"\t%p %p\n", real_malloc, real_free);
|
189
|
+
_exit(1);
|
190
|
+
}
|
191
|
+
#endif /* !__FreeBSD__ */
|
129
192
|
}
|
130
|
-
|
131
|
-
if (!
|
132
|
-
resolving_malloc = 1;
|
133
|
-
real_malloc = dlsym(RTLD_NEXT, "malloc");
|
134
|
-
}
|
135
|
-
real_free = dlsym(RTLD_NEXT, "free");
|
136
|
-
if (!real_malloc || !real_free) {
|
137
|
-
fprintf(stderr, "missing malloc/aligned_alloc/free\n"
|
138
|
-
"\t%p %p\n", real_malloc, real_free);
|
139
|
-
_exit(1);
|
140
|
-
}
|
141
|
-
#endif /* !FreeBSD */
|
142
|
-
totals = lfht_new();
|
143
|
-
if (!totals)
|
193
|
+
CMM_STORE_SHARED(totals, lfht_new());
|
194
|
+
if (!CMM_LOAD_SHARED(totals))
|
144
195
|
fprintf(stderr, "failed to allocate totals table\n");
|
145
196
|
|
146
197
|
err = pthread_atfork(call_rcu_before_fork,
|
@@ -152,11 +203,18 @@ __attribute__((constructor)) static void resolve_malloc(void)
|
|
152
203
|
--locating;
|
153
204
|
}
|
154
205
|
|
206
|
+
#ifdef NDEBUG
|
207
|
+
#define QUIET_CC_WARNING(var) (void)var;
|
208
|
+
#else
|
209
|
+
#define QUIET_CC_WARNING(var)
|
210
|
+
#endif
|
211
|
+
|
155
212
|
static void
|
156
213
|
mutex_lock(pthread_mutex_t *m)
|
157
214
|
{
|
158
215
|
int err = pthread_mutex_lock(m);
|
159
216
|
assert(err == 0);
|
217
|
+
QUIET_CC_WARNING(err)
|
160
218
|
}
|
161
219
|
|
162
220
|
static void
|
@@ -164,6 +222,7 @@ mutex_unlock(pthread_mutex_t *m)
|
|
164
222
|
{
|
165
223
|
int err = pthread_mutex_unlock(m);
|
166
224
|
assert(err == 0);
|
225
|
+
QUIET_CC_WARNING(err)
|
167
226
|
}
|
168
227
|
|
169
228
|
#ifndef HAVE_MEMPCPY
|
@@ -213,36 +272,10 @@ static char *int2str(int num, char *dst, size_t * size)
|
|
213
272
|
*/
|
214
273
|
static int has_ec_p(void)
|
215
274
|
{
|
216
|
-
return
|
217
|
-
|
275
|
+
return ruby_thread_has_gvl_p && ruby_thread_has_gvl_p() &&
|
276
|
+
ruby_current_vm_ptr && ruby_current_ec;
|
218
277
|
}
|
219
278
|
|
220
|
-
struct acc {
|
221
|
-
uint64_t nr;
|
222
|
-
int64_t min;
|
223
|
-
int64_t max;
|
224
|
-
double m2;
|
225
|
-
double mean;
|
226
|
-
};
|
227
|
-
|
228
|
-
#define ACC_INIT(name) { .nr=0, .min=INT64_MAX, .max=-1, .m2=0, .mean=0 }
|
229
|
-
|
230
|
-
/* for tracking 16K-aligned heap page bodies (protected by GVL) */
|
231
|
-
struct {
|
232
|
-
pthread_mutex_t lock;
|
233
|
-
struct cds_list_head bodies;
|
234
|
-
struct cds_list_head freed;
|
235
|
-
|
236
|
-
struct acc alive;
|
237
|
-
struct acc reborn;
|
238
|
-
} hpb_stats = {
|
239
|
-
.lock = PTHREAD_MUTEX_INITIALIZER,
|
240
|
-
.bodies = CDS_LIST_HEAD_INIT(hpb_stats.bodies),
|
241
|
-
.freed = CDS_LIST_HEAD_INIT(hpb_stats.freed),
|
242
|
-
.alive = ACC_INIT(hpb_stats.alive),
|
243
|
-
.reborn = ACC_INIT(hpb_stats.reborn)
|
244
|
-
};
|
245
|
-
|
246
279
|
/* allocated via real_malloc/real_free */
|
247
280
|
struct src_loc {
|
248
281
|
pthread_mutex_t *mtx;
|
@@ -367,7 +400,7 @@ acc_stddev(const struct acc *acc)
|
|
367
400
|
return DBL2NUM(acc_stddev_dbl(acc));
|
368
401
|
}
|
369
402
|
|
370
|
-
static struct src_loc *totals_add_rcu(struct src_loc *k)
|
403
|
+
static struct src_loc *totals_add_rcu(const struct src_loc *k)
|
371
404
|
{
|
372
405
|
struct cds_lfht_iter iter;
|
373
406
|
struct cds_lfht_node *cur;
|
@@ -375,7 +408,7 @@ static struct src_loc *totals_add_rcu(struct src_loc *k)
|
|
375
408
|
struct cds_lfht *t;
|
376
409
|
|
377
410
|
again:
|
378
|
-
t =
|
411
|
+
t = CMM_LOAD_SHARED(totals);
|
379
412
|
if (!t) goto out_unlock;
|
380
413
|
cds_lfht_lookup(t, k->hval, loc_eq, k, &iter);
|
381
414
|
cur = cds_lfht_iter_get_node(&iter);
|
@@ -417,7 +450,7 @@ static struct src_loc *update_stats_rcu_lock(size_t size, uintptr_t caller)
|
|
417
450
|
static const size_t xlen = sizeof(caller);
|
418
451
|
char *dst;
|
419
452
|
|
420
|
-
if (caa_unlikely(!totals)) return 0;
|
453
|
+
if (caa_unlikely(!CMM_LOAD_SHARED(totals))) return 0;
|
421
454
|
if (locating++) goto out; /* do not recurse into another *alloc */
|
422
455
|
|
423
456
|
uatomic_add(&total_bytes_inc, size);
|
@@ -632,9 +665,9 @@ internal_memalign(void **pp, size_t alignment, size_t size, uintptr_t caller)
|
|
632
665
|
p = ptr_align(p, alignment);
|
633
666
|
h = ptr2hdr(p);
|
634
667
|
alloc_insert_rcu(l, h, size, real);
|
635
|
-
update_stats_rcu_unlock(l);
|
636
668
|
*pp = p;
|
637
669
|
}
|
670
|
+
update_stats_rcu_unlock(l);
|
638
671
|
}
|
639
672
|
|
640
673
|
return real ? 0 : ENOMEM;
|
@@ -643,16 +676,14 @@ internal_memalign(void **pp, size_t alignment, size_t size, uintptr_t caller)
|
|
643
676
|
static void *
|
644
677
|
memalign_result(int err, void *p)
|
645
678
|
{
|
646
|
-
if (caa_unlikely(err))
|
679
|
+
if (caa_unlikely(err))
|
647
680
|
errno = err;
|
648
|
-
return 0;
|
649
|
-
}
|
650
681
|
return p;
|
651
682
|
}
|
652
683
|
|
653
684
|
void *memalign(size_t alignment, size_t size)
|
654
685
|
{
|
655
|
-
void *p;
|
686
|
+
void *p = NULL;
|
656
687
|
int err = internal_memalign(&p, alignment, size, RETURN_ADDRESS(0));
|
657
688
|
return memalign_result(err, p);
|
658
689
|
}
|
@@ -667,7 +698,7 @@ void cfree(void *) __attribute__((alias("free")));
|
|
667
698
|
|
668
699
|
void *valloc(size_t size)
|
669
700
|
{
|
670
|
-
void *p;
|
701
|
+
void *p = NULL;
|
671
702
|
int err = internal_memalign(&p, page_size, size, RETURN_ADDRESS(0));
|
672
703
|
return memalign_result(err, p);
|
673
704
|
}
|
@@ -685,7 +716,7 @@ void *valloc(size_t size)
|
|
685
716
|
void *pvalloc(size_t size)
|
686
717
|
{
|
687
718
|
size_t alignment = page_size;
|
688
|
-
void *p;
|
719
|
+
void *p = NULL;
|
689
720
|
int err;
|
690
721
|
|
691
722
|
if (add_overflow_p(size, alignment)) {
|
@@ -808,7 +839,7 @@ static void *dump_to_file(void *x)
|
|
808
839
|
|
809
840
|
++locating;
|
810
841
|
rcu_read_lock();
|
811
|
-
t =
|
842
|
+
t = CMM_LOAD_SHARED(totals);
|
812
843
|
if (!t)
|
813
844
|
goto out_unlock;
|
814
845
|
cds_lfht_for_each_entry(t, &iter, l, hnode) {
|
@@ -877,7 +908,7 @@ static void *totals_reset(void *ign)
|
|
877
908
|
uatomic_set(&total_bytes_dec, 0);
|
878
909
|
|
879
910
|
rcu_read_lock();
|
880
|
-
t =
|
911
|
+
t = CMM_LOAD_SHARED(totals);
|
881
912
|
cds_lfht_for_each_entry(t, &iter, l, hnode) {
|
882
913
|
uatomic_set(&l->total, 0);
|
883
914
|
uatomic_set(&l->allocations, 0);
|
@@ -945,7 +976,7 @@ static VALUE dump_each_rcu(VALUE x)
|
|
945
976
|
struct cds_lfht_iter iter;
|
946
977
|
struct src_loc *l;
|
947
978
|
|
948
|
-
t =
|
979
|
+
t = CMM_LOAD_SHARED(totals);
|
949
980
|
cds_lfht_for_each_entry(t, &iter, l, hnode) {
|
950
981
|
VALUE v[6];
|
951
982
|
if (l->total <= a->min) continue;
|
@@ -1049,9 +1080,9 @@ static VALUE mwrap_aref(VALUE mod, VALUE loc)
|
|
1049
1080
|
|
1050
1081
|
if (!k) return val;
|
1051
1082
|
|
1083
|
+
t = CMM_LOAD_SHARED(totals);
|
1084
|
+
if (!t) return val;
|
1052
1085
|
rcu_read_lock();
|
1053
|
-
t = rcu_dereference(totals);
|
1054
|
-
if (!t) goto out_unlock;
|
1055
1086
|
|
1056
1087
|
cds_lfht_lookup(t, k->hval, loc_eq, k, &iter);
|
1057
1088
|
cur = cds_lfht_iter_get_node(&iter);
|
@@ -1059,7 +1090,6 @@ static VALUE mwrap_aref(VALUE mod, VALUE loc)
|
|
1059
1090
|
l = caa_container_of(cur, struct src_loc, hnode);
|
1060
1091
|
val = TypedData_Wrap_Struct(cSrcLoc, &src_loc_type, l);
|
1061
1092
|
}
|
1062
|
-
out_unlock:
|
1063
1093
|
rcu_read_unlock();
|
1064
1094
|
return val;
|
1065
1095
|
}
|
@@ -0,0 +1 @@
|
|
1
|
+
version.rb
|
@@ -0,0 +1 @@
|
|
1
|
+
module Mwrap; VERSION = '2.3.0'.freeze; end
|
data/lib/mwrap_rack.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C)
|
1
|
+
# Copyright (C) all contributors <mwrap-public@80x24.org>
|
2
2
|
# License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
|
3
3
|
# frozen_string_literal: true
|
4
4
|
require 'mwrap'
|
@@ -17,9 +17,6 @@ require 'cgi'
|
|
17
17
|
# map('/MWRAP') { run(MwrapRack.new) }
|
18
18
|
# map('/') { run(your_normal_app) }
|
19
19
|
#
|
20
|
-
# A live demo is available at https://80x24.org/MWRAP/
|
21
|
-
# (warning the demo machine is 32-bit, so counters will overflow)
|
22
|
-
#
|
23
20
|
# This module is only available in mwrap 2.0.0+
|
24
21
|
class MwrapRack
|
25
22
|
module HtmlResponse # :nodoc:
|
@@ -115,10 +112,11 @@ class MwrapRack
|
|
115
112
|
end
|
116
113
|
|
117
114
|
GC_STAT_URL = 'https://docs.ruby-lang.org/en/trunk/GC.html#method-c-stat'
|
118
|
-
GC_STAT_HELP = <<~
|
115
|
+
GC_STAT_HELP = <<~EOM
|
119
116
|
<p>Non-Infinity lifespans can indicate fragmentation.
|
120
117
|
<p>See <a
|
121
118
|
href="#{GC_STAT_URL}">#{GC_STAT_URL}</a> for info on GC.stat values.
|
119
|
+
EOM
|
122
120
|
|
123
121
|
def each
|
124
122
|
Mwrap.quiet do
|
data/mwrap.gemspec
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
git_manifest = `git ls-files 2>/dev/null`.split("\n")
|
2
|
+
git_ok = $?.success?
|
3
|
+
git_manifest << 'lib/mwrap/version.rb'.freeze # generated by ./VERSION-GEN
|
2
4
|
manifest = File.exist?('MANIFEST') ?
|
3
5
|
File.readlines('MANIFEST').map!(&:chomp).delete_if(&:empty?) : git_manifest
|
4
|
-
if
|
6
|
+
if git_ok && manifest != git_manifest
|
5
7
|
tmp = "MANIFEST.#$$.tmp"
|
6
8
|
File.open(tmp, 'w') { |fp| fp.puts(git_manifest.join("\n")) }
|
7
9
|
File.rename(tmp, 'MANIFEST')
|
8
10
|
system('git add MANIFEST')
|
9
11
|
end
|
10
12
|
|
11
|
-
|
13
|
+
version = `./VERSION-GEN`.chomp
|
14
|
+
$?.success? or abort './VERSION-GEN failed'
|
12
15
|
|
13
16
|
Gem::Specification.new do |s|
|
14
17
|
s.name = 'mwrap'
|
15
|
-
s.version =
|
18
|
+
s.version = version
|
16
19
|
s.homepage = 'https://80x24.org/mwrap/'
|
17
|
-
s.authors = ["
|
20
|
+
s.authors = ["mwrap hackers"]
|
18
21
|
s.summary = 'LD_PRELOAD malloc wrapper for Ruby'
|
19
22
|
s.executables = %w(mwrap)
|
20
23
|
s.files = manifest
|
data/test/test_mwrap.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
# Copyright (C)
|
2
|
+
# Copyright (C) mwrap hackers <mwrap-public@80x24.org>
|
3
3
|
# License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
|
4
4
|
require 'test/unit'
|
5
5
|
require 'mwrap'
|
@@ -29,7 +29,8 @@ class TestMwrap < Test::Unit::TestCase
|
|
29
29
|
tmp.rewind
|
30
30
|
lines = tmp.readlines
|
31
31
|
line_1 = lines.grep(/\s-e:1\b/)[0].strip
|
32
|
-
|
32
|
+
bytes = line_1.split(/\s+/)[0].to_i
|
33
|
+
assert_operator bytes, :>=, 10001
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
@@ -42,7 +43,7 @@ class TestMwrap < Test::Unit::TestCase
|
|
42
43
|
res = system(env, *cmd, { 5 => tmp })
|
43
44
|
assert res, $?.inspect
|
44
45
|
tmp.rewind
|
45
|
-
assert_match(/\
|
46
|
+
assert_match(/\b1\d{4}\s+[1-9]\d*\s+-e:1$/, tmp.read)
|
46
47
|
|
47
48
|
env['MWRAP'] = 'dump_fd:1,dump_min:10000'
|
48
49
|
tmp.rewind
|
@@ -50,14 +51,14 @@ class TestMwrap < Test::Unit::TestCase
|
|
50
51
|
res = system(env, *cmd, { 1 => tmp })
|
51
52
|
assert res, $?.inspect
|
52
53
|
tmp.rewind
|
53
|
-
assert_match(/\
|
54
|
+
assert_match(/\b1\d{4}\s+[1-9]\d*\s+-e:1$/, tmp.read)
|
54
55
|
|
55
56
|
tmp.rewind
|
56
57
|
tmp.truncate(0)
|
57
58
|
env['MWRAP'] = "dump_path:#{tmp.path},dump_min:10000"
|
58
59
|
res = system(env, *cmd)
|
59
60
|
assert res, $?.inspect
|
60
|
-
assert_match(/\
|
61
|
+
assert_match(/\b1\d{4}\s+[1-9]\d*\s+-e:1$/, tmp.read)
|
61
62
|
|
62
63
|
tmp.rewind
|
63
64
|
tmp.truncate(0)
|
@@ -98,7 +99,7 @@ class TestMwrap < Test::Unit::TestCase
|
|
98
99
|
tmp.rewind
|
99
100
|
buf = tmp.read
|
100
101
|
assert_not_match(/\s+-e:1$/, buf)
|
101
|
-
assert_match(/\
|
102
|
+
assert_match(/\b2\d{4}\s+[0-9]\d*\s+-e:3$/, buf)
|
102
103
|
end
|
103
104
|
end
|
104
105
|
|
@@ -176,8 +177,8 @@ class TestMwrap < Test::Unit::TestCase
|
|
176
177
|
-e GC.disable
|
177
178
|
-e keep=("0"*10000)
|
178
179
|
-e loc=Mwrap["-e:3"]
|
179
|
-
-e
|
180
|
-
)
|
180
|
+
-e
|
181
|
+
) + [ 'loc.each{|size,gen|p([size,gen,count]) if size > 10000}' ]
|
181
182
|
buf = IO.popen(@@env, cmd, &:read)
|
182
183
|
assert_predicate $?, :success?
|
183
184
|
assert_match(/\A\[\s*\d+,\s*\d+,\s*\d+\]\s*\z/s, buf)
|
@@ -230,7 +231,8 @@ class TestMwrap < Test::Unit::TestCase
|
|
230
231
|
loc.name == k or abort 'SourceLocation#name broken'
|
231
232
|
loc.total >= 10000 or abort 'SourceLocation#total broken'
|
232
233
|
loc.frees == 0 or abort 'SourceLocation#frees broken'
|
233
|
-
loc.allocations
|
234
|
+
loc.allocations >= 1 or
|
235
|
+
abort "SourceLocation#allocations broken: #{loc.allocations}"
|
234
236
|
seen = false
|
235
237
|
loc.each do |*x| seen = x end
|
236
238
|
seen[1] == loc.total or 'SourceLocation#each broken'
|
@@ -240,7 +242,9 @@ class TestMwrap < Test::Unit::TestCase
|
|
240
242
|
freed = false
|
241
243
|
until freed
|
242
244
|
freed = true
|
243
|
-
loc.each do
|
245
|
+
loc.each do |size, gen|
|
246
|
+
freed = false if size >= 10000
|
247
|
+
end
|
244
248
|
end
|
245
249
|
loc.frees == 1 or abort 'SourceLocation#frees broken (after free)'
|
246
250
|
Float === loc.mean_lifespan or abort 'mean_lifespan broken'
|
@@ -264,8 +268,9 @@ class TestMwrap < Test::Unit::TestCase
|
|
264
268
|
assert_separately(+"#{<<~"begin;"}\n#{<<~'end;'}")
|
265
269
|
begin;
|
266
270
|
require 'mwrap'
|
267
|
-
before =
|
271
|
+
before = nil
|
268
272
|
res = Mwrap.quiet do |depth|
|
273
|
+
before = __LINE__
|
269
274
|
depth == 1 or abort 'depth is not 1'
|
270
275
|
('a' * 10000).clear
|
271
276
|
Mwrap.quiet { |d| d == 2 or abort 'depth is not 2' }
|
@@ -304,7 +309,9 @@ class TestMwrap < Test::Unit::TestCase
|
|
304
309
|
gen <= GC.count && gen >= 0 or abort "bad generation: #{gen}"
|
305
310
|
(0 == (addr & 16383)) or abort "addr not aligned: #{'%x' % addr}"
|
306
311
|
end
|
307
|
-
|
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
|
308
315
|
10.times { (1..20000).to_a.map(&:to_s) }
|
309
316
|
3.times { GC.start }
|
310
317
|
Mwrap::HeapPageBody.stat(h)
|
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.
|
4
|
+
version: 2.3.0
|
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: 2022-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: test-unit
|
@@ -55,10 +55,13 @@ files:
|
|
55
55
|
- MANIFEST
|
56
56
|
- README
|
57
57
|
- Rakefile
|
58
|
+
- VERSION-GEN
|
58
59
|
- bin/mwrap
|
59
60
|
- ext/mwrap/extconf.rb
|
60
61
|
- ext/mwrap/jhash.h
|
61
62
|
- ext/mwrap/mwrap.c
|
63
|
+
- lib/mwrap/.gitignore
|
64
|
+
- lib/mwrap/version.rb
|
62
65
|
- lib/mwrap_rack.rb
|
63
66
|
- mwrap.gemspec
|
64
67
|
- test/test_mwrap.rb
|
@@ -81,8 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
84
|
- !ruby/object:Gem::Version
|
82
85
|
version: '0'
|
83
86
|
requirements: []
|
84
|
-
|
85
|
-
rubygems_version: 2.7.7
|
87
|
+
rubygems_version: 3.0.2
|
86
88
|
signing_key:
|
87
89
|
specification_version: 4
|
88
90
|
summary: LD_PRELOAD malloc wrapper for Ruby
|