mwrap 2.2.0.1.g867b → 3.0.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/COPYING +617 -282
- data/Documentation/.gitignore +2 -0
- data/Documentation/GNUmakefile +63 -0
- data/Documentation/mwrap.1 +242 -0
- data/Documentation/mwrap.pod +123 -0
- data/MANIFEST +15 -0
- data/README +25 -17
- data/Rakefile +10 -2
- data/VERSION-GEN +36 -0
- data/bin/mwrap +19 -0
- data/ext/mwrap/check.h +23 -0
- data/ext/mwrap/dlmalloc_c.h +6294 -0
- data/ext/mwrap/extconf.rb +7 -11
- data/ext/mwrap/gcc.h +13 -0
- data/ext/mwrap/httpd.h +1367 -0
- data/ext/mwrap/mwrap.c +44 -1138
- data/ext/mwrap/mwrap_core.h +1095 -0
- data/ext/mwrap/mymalloc.h +299 -0
- data/ext/mwrap/picohttpparser.h +92 -0
- data/ext/mwrap/picohttpparser_c.h +670 -0
- data/lib/mwrap/.gitignore +1 -0
- data/lib/mwrap/version.rb +1 -0
- data/lib/mwrap_rack.rb +14 -58
- data/mwrap.gemspec +15 -5
- data/t/httpd.t +191 -0
- data/t/test_common.perl +54 -0
- data/test/test_mwrap.rb +34 -50
- metadata +22 -5
data/ext/mwrap/mwrap.c
CHANGED
@@ -2,851 +2,14 @@
|
|
2
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
|
-
#define
|
6
|
-
#include
|
7
|
-
#include <ruby/thread.h>
|
8
|
-
#include <ruby/io.h>
|
9
|
-
#include <execinfo.h>
|
10
|
-
#include <stdio.h>
|
11
|
-
#include <stdlib.h>
|
12
|
-
#include <string.h>
|
13
|
-
#include <dlfcn.h>
|
14
|
-
#include <assert.h>
|
15
|
-
#include <errno.h>
|
16
|
-
#include <sys/types.h>
|
17
|
-
#include <sys/stat.h>
|
18
|
-
#include <fcntl.h>
|
19
|
-
#include <pthread.h>
|
20
|
-
#include <urcu-bp.h>
|
21
|
-
#include <urcu/rculfhash.h>
|
22
|
-
#include <urcu/rculist.h>
|
23
|
-
#include "jhash.h"
|
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
|
5
|
+
#define MWRAP_RUBY 1
|
6
|
+
#include "mwrap_core.h"
|
32
7
|
|
33
8
|
static ID id_uminus;
|
34
|
-
|
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 */
|
39
|
-
extern void * __attribute__((weak)) ruby_current_execution_context_ptr;
|
40
|
-
# define ruby_current_ec ruby_current_execution_context_ptr
|
41
|
-
#endif
|
42
|
-
extern void * __attribute__((weak)) ruby_current_vm_ptr; /* for rb_gc_count */
|
43
|
-
extern size_t __attribute__((weak)) rb_gc_count(void);
|
9
|
+
extern VALUE __attribute__((weak)) rb_stderr;
|
44
10
|
extern VALUE __attribute__((weak)) rb_cObject;
|
45
11
|
extern VALUE __attribute__((weak)) rb_eTypeError;
|
46
12
|
extern VALUE __attribute__((weak)) rb_yield(VALUE);
|
47
|
-
int __attribute__((weak)) ruby_thread_has_gvl_p(void);
|
48
|
-
|
49
|
-
static size_t total_bytes_inc, total_bytes_dec;
|
50
|
-
|
51
|
-
/* true for glibc/dlmalloc/ptmalloc, not sure about jemalloc */
|
52
|
-
#define ASSUMED_MALLOC_ALIGNMENT (sizeof(void *) * 2)
|
53
|
-
|
54
|
-
/* match values in Ruby gc.c */
|
55
|
-
#define HEAP_PAGE_ALIGN_LOG 14
|
56
|
-
enum {
|
57
|
-
HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG)
|
58
|
-
#ifndef HEAP_PAGE_SIZE /* Ruby 2.6-2.7 only */
|
59
|
-
,
|
60
|
-
REQUIRED_SIZE_BY_MALLOC = (sizeof(size_t) * 5),
|
61
|
-
HEAP_PAGE_SIZE = (HEAP_PAGE_ALIGN - REQUIRED_SIZE_BY_MALLOC)
|
62
|
-
#endif
|
63
|
-
};
|
64
|
-
|
65
|
-
#define IS_HEAP_PAGE_BODY ((struct src_loc *)-1)
|
66
|
-
|
67
|
-
#ifdef __FreeBSD__
|
68
|
-
void *__malloc(size_t);
|
69
|
-
void __free(void *);
|
70
|
-
# define real_malloc __malloc
|
71
|
-
# define real_free __free
|
72
|
-
#else
|
73
|
-
static void *(*real_malloc)(size_t);
|
74
|
-
static void (*real_free)(void *);
|
75
|
-
static int resolving_malloc;
|
76
|
-
#endif /* !FreeBSD */
|
77
|
-
|
78
|
-
/*
|
79
|
-
* we need to fake an OOM condition while dlsym is running,
|
80
|
-
* as that calls calloc under glibc, but we don't have the
|
81
|
-
* symbol for the jemalloc calloc, yet
|
82
|
-
*/
|
83
|
-
# define RETURN_IF_NOT_READY() do { \
|
84
|
-
if (!real_malloc) { \
|
85
|
-
errno = ENOMEM; \
|
86
|
-
return NULL; \
|
87
|
-
} \
|
88
|
-
} while (0)
|
89
|
-
|
90
|
-
static MWRAP_TSD size_t locating;
|
91
|
-
static size_t generation;
|
92
|
-
static size_t page_size;
|
93
|
-
static struct cds_lfht *totals;
|
94
|
-
union padded_mutex {
|
95
|
-
pthread_mutex_t mtx;
|
96
|
-
char pad[64];
|
97
|
-
};
|
98
|
-
|
99
|
-
/* a round-robin pool of mutexes */
|
100
|
-
#define MUTEX_NR (1 << 6)
|
101
|
-
#define MUTEX_MASK (MUTEX_NR - 1)
|
102
|
-
static size_t mutex_i;
|
103
|
-
static union padded_mutex mutexes[MUTEX_NR] = {
|
104
|
-
[0 ... (MUTEX_NR-1)].mtx = PTHREAD_MUTEX_INITIALIZER
|
105
|
-
};
|
106
|
-
|
107
|
-
static pthread_mutex_t *mutex_assign(void)
|
108
|
-
{
|
109
|
-
return &mutexes[uatomic_add_return(&mutex_i, 1) & MUTEX_MASK].mtx;
|
110
|
-
}
|
111
|
-
|
112
|
-
static struct cds_lfht *
|
113
|
-
lfht_new(void)
|
114
|
-
{
|
115
|
-
return cds_lfht_new(16384, 1, 0, CDS_LFHT_AUTO_RESIZE, 0);
|
116
|
-
}
|
117
|
-
|
118
|
-
__attribute__((constructor)) static void resolve_malloc(void)
|
119
|
-
{
|
120
|
-
int err;
|
121
|
-
++locating;
|
122
|
-
|
123
|
-
#ifdef __FreeBSD__
|
124
|
-
/*
|
125
|
-
* PTHREAD_MUTEX_INITIALIZER on FreeBSD means lazy initialization,
|
126
|
-
* which happens at pthread_mutex_lock, and that calls calloc
|
127
|
-
*/
|
128
|
-
{
|
129
|
-
size_t i;
|
130
|
-
|
131
|
-
for (i = 0; i < MUTEX_NR; i++) {
|
132
|
-
err = pthread_mutex_init(&mutexes[i].mtx, 0);
|
133
|
-
if (err) {
|
134
|
-
fprintf(stderr, "error: %s\n", strerror(err));
|
135
|
-
_exit(1);
|
136
|
-
}
|
137
|
-
}
|
138
|
-
/* initialize mutexes used by urcu-bp */
|
139
|
-
rcu_read_lock();
|
140
|
-
rcu_read_unlock();
|
141
|
-
}
|
142
|
-
#else /* !FreeBSD (tested on GNU/Linux) */
|
143
|
-
if (!real_malloc) {
|
144
|
-
resolving_malloc = 1;
|
145
|
-
real_malloc = dlsym(RTLD_NEXT, "malloc");
|
146
|
-
}
|
147
|
-
real_free = dlsym(RTLD_NEXT, "free");
|
148
|
-
if (!real_malloc || !real_free) {
|
149
|
-
fprintf(stderr, "missing malloc/aligned_alloc/free\n"
|
150
|
-
"\t%p %p\n", real_malloc, real_free);
|
151
|
-
_exit(1);
|
152
|
-
}
|
153
|
-
#endif /* !FreeBSD */
|
154
|
-
CMM_STORE_SHARED(totals, lfht_new());
|
155
|
-
if (!CMM_LOAD_SHARED(totals))
|
156
|
-
fprintf(stderr, "failed to allocate totals table\n");
|
157
|
-
|
158
|
-
err = pthread_atfork(call_rcu_before_fork,
|
159
|
-
call_rcu_after_fork_parent,
|
160
|
-
call_rcu_after_fork_child);
|
161
|
-
if (err)
|
162
|
-
fprintf(stderr, "pthread_atfork failed: %s\n", strerror(err));
|
163
|
-
page_size = sysconf(_SC_PAGESIZE);
|
164
|
-
--locating;
|
165
|
-
}
|
166
|
-
|
167
|
-
#ifdef NDEBUG
|
168
|
-
#define QUIET_CC_WARNING(var) (void)var;
|
169
|
-
#else
|
170
|
-
#define QUIET_CC_WARNING(var)
|
171
|
-
#endif
|
172
|
-
|
173
|
-
static void
|
174
|
-
mutex_lock(pthread_mutex_t *m)
|
175
|
-
{
|
176
|
-
int err = pthread_mutex_lock(m);
|
177
|
-
assert(err == 0);
|
178
|
-
QUIET_CC_WARNING(err)
|
179
|
-
}
|
180
|
-
|
181
|
-
static void
|
182
|
-
mutex_unlock(pthread_mutex_t *m)
|
183
|
-
{
|
184
|
-
int err = pthread_mutex_unlock(m);
|
185
|
-
assert(err == 0);
|
186
|
-
QUIET_CC_WARNING(err)
|
187
|
-
}
|
188
|
-
|
189
|
-
#ifndef HAVE_MEMPCPY
|
190
|
-
static void *
|
191
|
-
my_mempcpy(void *dest, const void *src, size_t n)
|
192
|
-
{
|
193
|
-
return (char *)memcpy(dest, src, n) + n;
|
194
|
-
}
|
195
|
-
#define mempcpy(dst,src,n) my_mempcpy(dst,src,n)
|
196
|
-
#endif
|
197
|
-
|
198
|
-
/* stolen from glibc: */
|
199
|
-
#define RETURN_ADDRESS(nr) \
|
200
|
-
(uintptr_t)(__builtin_extract_return_addr(__builtin_return_address(nr)))
|
201
|
-
|
202
|
-
#define INT2STR_MAX (sizeof(int) == 4 ? 10 : 19)
|
203
|
-
static char *int2str(int num, char *dst, size_t * size)
|
204
|
-
{
|
205
|
-
if (num <= 9) {
|
206
|
-
*size -= 1;
|
207
|
-
*dst++ = (char)(num + '0');
|
208
|
-
return dst;
|
209
|
-
} else {
|
210
|
-
char buf[INT2STR_MAX];
|
211
|
-
char *end = buf + sizeof(buf);
|
212
|
-
char *p = end;
|
213
|
-
size_t adj;
|
214
|
-
|
215
|
-
do {
|
216
|
-
*size -= 1;
|
217
|
-
*--p = (char)((num % 10) + '0');
|
218
|
-
num /= 10;
|
219
|
-
} while (num && *size);
|
220
|
-
|
221
|
-
if (!num) {
|
222
|
-
adj = end - p;
|
223
|
-
return mempcpy(dst, p, adj);
|
224
|
-
}
|
225
|
-
}
|
226
|
-
return NULL;
|
227
|
-
}
|
228
|
-
|
229
|
-
/*
|
230
|
-
* rb_source_location_cstr relies on GET_EC(), and it's possible
|
231
|
-
* to have a native thread but no EC during the early and late
|
232
|
-
* (teardown) phases of the Ruby process
|
233
|
-
*/
|
234
|
-
static int has_ec_p(void)
|
235
|
-
{
|
236
|
-
return ruby_thread_has_gvl_p && ruby_thread_has_gvl_p() &&
|
237
|
-
ruby_current_vm_ptr && ruby_current_ec;
|
238
|
-
}
|
239
|
-
|
240
|
-
struct acc {
|
241
|
-
uint64_t nr;
|
242
|
-
int64_t min;
|
243
|
-
int64_t max;
|
244
|
-
double m2;
|
245
|
-
double mean;
|
246
|
-
};
|
247
|
-
|
248
|
-
#define ACC_INIT(name) { .nr=0, .min=INT64_MAX, .max=-1, .m2=0, .mean=0 }
|
249
|
-
|
250
|
-
/* for tracking 16K-aligned heap page bodies (protected by GVL) */
|
251
|
-
struct {
|
252
|
-
pthread_mutex_t lock;
|
253
|
-
struct cds_list_head bodies;
|
254
|
-
struct cds_list_head freed;
|
255
|
-
|
256
|
-
struct acc alive;
|
257
|
-
struct acc reborn;
|
258
|
-
} hpb_stats = {
|
259
|
-
.lock = PTHREAD_MUTEX_INITIALIZER,
|
260
|
-
.bodies = CDS_LIST_HEAD_INIT(hpb_stats.bodies),
|
261
|
-
.freed = CDS_LIST_HEAD_INIT(hpb_stats.freed),
|
262
|
-
.alive = ACC_INIT(hpb_stats.alive),
|
263
|
-
.reborn = ACC_INIT(hpb_stats.reborn)
|
264
|
-
};
|
265
|
-
|
266
|
-
/* allocated via real_malloc/real_free */
|
267
|
-
struct src_loc {
|
268
|
-
pthread_mutex_t *mtx;
|
269
|
-
size_t total;
|
270
|
-
size_t allocations;
|
271
|
-
size_t frees;
|
272
|
-
size_t age_total; /* (age_total / frees) => mean age at free */
|
273
|
-
size_t max_lifespan;
|
274
|
-
struct cds_lfht_node hnode;
|
275
|
-
struct cds_list_head allocs; /* <=> alloc_hdr.node */
|
276
|
-
uint32_t hval;
|
277
|
-
uint32_t capa;
|
278
|
-
char k[];
|
279
|
-
};
|
280
|
-
|
281
|
-
/* every allocation has this in the header, maintain alignment with malloc */
|
282
|
-
struct alloc_hdr {
|
283
|
-
struct cds_list_head anode; /* <=> src_loc.allocs */
|
284
|
-
union {
|
285
|
-
struct {
|
286
|
-
size_t gen; /* rb_gc_count() */
|
287
|
-
struct src_loc *loc;
|
288
|
-
} live;
|
289
|
-
struct rcu_head dead;
|
290
|
-
struct {
|
291
|
-
size_t at; /* rb_gc_count() */
|
292
|
-
} hpb_freed;
|
293
|
-
} as;
|
294
|
-
void *real; /* what to call real_free on */
|
295
|
-
size_t size;
|
296
|
-
};
|
297
|
-
|
298
|
-
static char kbuf[PATH_MAX + INT2STR_MAX + sizeof(struct alloc_hdr) + 2];
|
299
|
-
|
300
|
-
static struct alloc_hdr *ptr2hdr(void *p)
|
301
|
-
{
|
302
|
-
return (struct alloc_hdr *)((uintptr_t)p - sizeof(struct alloc_hdr));
|
303
|
-
}
|
304
|
-
|
305
|
-
static void *hdr2ptr(struct alloc_hdr *h)
|
306
|
-
{
|
307
|
-
return (void *)((uintptr_t)h + sizeof(struct alloc_hdr));
|
308
|
-
}
|
309
|
-
|
310
|
-
static int loc_is_addr(const struct src_loc *l)
|
311
|
-
{
|
312
|
-
return l->capa == 0;
|
313
|
-
}
|
314
|
-
|
315
|
-
static size_t loc_size(const struct src_loc *l)
|
316
|
-
{
|
317
|
-
return loc_is_addr(l) ? sizeof(uintptr_t) : l->capa;
|
318
|
-
}
|
319
|
-
|
320
|
-
static int loc_eq(struct cds_lfht_node *node, const void *key)
|
321
|
-
{
|
322
|
-
const struct src_loc *existing;
|
323
|
-
const struct src_loc *k = key;
|
324
|
-
|
325
|
-
existing = caa_container_of(node, struct src_loc, hnode);
|
326
|
-
|
327
|
-
return (k->hval == existing->hval &&
|
328
|
-
k->capa == existing->capa &&
|
329
|
-
memcmp(k->k, existing->k, loc_size(k)) == 0);
|
330
|
-
}
|
331
|
-
|
332
|
-
/* note: not atomic */
|
333
|
-
static void
|
334
|
-
acc_add(struct acc *acc, size_t val)
|
335
|
-
{
|
336
|
-
double delta = val - acc->mean;
|
337
|
-
uint64_t nr = ++acc->nr;
|
338
|
-
|
339
|
-
/* just don't divide-by-zero if we ever hit this (unlikely :P) */
|
340
|
-
if (nr)
|
341
|
-
acc->mean += delta / nr;
|
342
|
-
|
343
|
-
acc->m2 += delta * (val - acc->mean);
|
344
|
-
if ((int64_t)val < acc->min)
|
345
|
-
acc->min = (int64_t)val;
|
346
|
-
if ((int64_t)val > acc->max)
|
347
|
-
acc->max = (int64_t)val;
|
348
|
-
}
|
349
|
-
|
350
|
-
#if SIZEOF_LONG == 8
|
351
|
-
# define INT64toNUM(x) LONG2NUM((long)x)
|
352
|
-
#elif defined(HAVE_LONG_LONG) && SIZEOF_LONG_LONG == 8
|
353
|
-
# define INT64toNUM(x) LL2NUM((LONG_LONG)x)
|
354
|
-
#endif
|
355
|
-
|
356
|
-
static VALUE
|
357
|
-
acc_max(const struct acc *acc)
|
358
|
-
{
|
359
|
-
return INT64toNUM(acc->max);
|
360
|
-
}
|
361
|
-
|
362
|
-
static VALUE
|
363
|
-
acc_min(const struct acc *acc)
|
364
|
-
{
|
365
|
-
return acc->min == INT64_MAX ? INT2FIX(-1) : INT64toNUM(acc->min);
|
366
|
-
}
|
367
|
-
|
368
|
-
static VALUE
|
369
|
-
acc_mean(const struct acc *acc)
|
370
|
-
{
|
371
|
-
return DBL2NUM(acc->nr ? acc->mean : HUGE_VAL);
|
372
|
-
}
|
373
|
-
|
374
|
-
static double
|
375
|
-
acc_stddev_dbl(const struct acc *acc)
|
376
|
-
{
|
377
|
-
if (acc->nr > 1) {
|
378
|
-
double variance = acc->m2 / (acc->nr - 1);
|
379
|
-
return sqrt(variance);
|
380
|
-
}
|
381
|
-
return 0.0;
|
382
|
-
}
|
383
|
-
|
384
|
-
static VALUE
|
385
|
-
acc_stddev(const struct acc *acc)
|
386
|
-
{
|
387
|
-
return DBL2NUM(acc_stddev_dbl(acc));
|
388
|
-
}
|
389
|
-
|
390
|
-
static struct src_loc *totals_add_rcu(const struct src_loc *k)
|
391
|
-
{
|
392
|
-
struct cds_lfht_iter iter;
|
393
|
-
struct cds_lfht_node *cur;
|
394
|
-
struct src_loc *l = 0;
|
395
|
-
struct cds_lfht *t;
|
396
|
-
|
397
|
-
again:
|
398
|
-
t = CMM_LOAD_SHARED(totals);
|
399
|
-
if (!t) goto out_unlock;
|
400
|
-
cds_lfht_lookup(t, k->hval, loc_eq, k, &iter);
|
401
|
-
cur = cds_lfht_iter_get_node(&iter);
|
402
|
-
if (cur) {
|
403
|
-
l = caa_container_of(cur, struct src_loc, hnode);
|
404
|
-
uatomic_add(&l->total, k->total);
|
405
|
-
uatomic_add(&l->allocations, 1);
|
406
|
-
} else {
|
407
|
-
size_t n = loc_size(k);
|
408
|
-
l = real_malloc(sizeof(*l) + n);
|
409
|
-
if (!l) goto out_unlock;
|
410
|
-
memcpy(l, k, sizeof(*l) + n);
|
411
|
-
l->mtx = mutex_assign();
|
412
|
-
l->age_total = 0;
|
413
|
-
l->max_lifespan = 0;
|
414
|
-
l->frees = 0;
|
415
|
-
l->allocations = 1;
|
416
|
-
CDS_INIT_LIST_HEAD(&l->allocs);
|
417
|
-
cur = cds_lfht_add_unique(t, k->hval, loc_eq, l, &l->hnode);
|
418
|
-
if (cur != &l->hnode) { /* lost race */
|
419
|
-
rcu_read_unlock();
|
420
|
-
real_free(l);
|
421
|
-
rcu_read_lock();
|
422
|
-
goto again;
|
423
|
-
}
|
424
|
-
}
|
425
|
-
out_unlock:
|
426
|
-
return l;
|
427
|
-
}
|
428
|
-
|
429
|
-
static void update_stats_rcu_unlock(const struct src_loc *l)
|
430
|
-
{
|
431
|
-
if (caa_likely(l)) rcu_read_unlock();
|
432
|
-
}
|
433
|
-
|
434
|
-
static struct src_loc *update_stats_rcu_lock(size_t size, uintptr_t caller)
|
435
|
-
{
|
436
|
-
struct src_loc *k, *ret = 0;
|
437
|
-
static const size_t xlen = sizeof(caller);
|
438
|
-
char *dst;
|
439
|
-
|
440
|
-
if (caa_unlikely(!CMM_LOAD_SHARED(totals))) return 0;
|
441
|
-
if (locating++) goto out; /* do not recurse into another *alloc */
|
442
|
-
|
443
|
-
uatomic_add(&total_bytes_inc, size);
|
444
|
-
|
445
|
-
rcu_read_lock();
|
446
|
-
if (has_ec_p()) {
|
447
|
-
int line;
|
448
|
-
const char *ptr = rb_source_location_cstr(&line);
|
449
|
-
size_t len;
|
450
|
-
size_t int_size = INT2STR_MAX;
|
451
|
-
|
452
|
-
generation = rb_gc_count();
|
453
|
-
|
454
|
-
if (!ptr) goto unknown;
|
455
|
-
|
456
|
-
/* avoid vsnprintf or anything which could call malloc here: */
|
457
|
-
len = strlen(ptr);
|
458
|
-
k = (void *)kbuf;
|
459
|
-
k->total = size;
|
460
|
-
dst = mempcpy(k->k, ptr, len);
|
461
|
-
*dst++ = ':';
|
462
|
-
dst = int2str(line, dst, &int_size);
|
463
|
-
if (dst) {
|
464
|
-
*dst = 0; /* terminate string */
|
465
|
-
k->capa = (uint32_t)(dst - k->k + 1);
|
466
|
-
k->hval = jhash(k->k, k->capa, 0xdeadbeef);
|
467
|
-
ret = totals_add_rcu(k);
|
468
|
-
} else {
|
469
|
-
rb_bug("bad math making key from location %s:%d\n",
|
470
|
-
ptr, line);
|
471
|
-
}
|
472
|
-
} else {
|
473
|
-
unknown:
|
474
|
-
k = alloca(sizeof(*k) + xlen);
|
475
|
-
k->total = size;
|
476
|
-
memcpy(k->k, &caller, xlen);
|
477
|
-
k->capa = 0;
|
478
|
-
k->hval = jhash(k->k, xlen, 0xdeadbeef);
|
479
|
-
ret = totals_add_rcu(k);
|
480
|
-
}
|
481
|
-
out:
|
482
|
-
--locating;
|
483
|
-
return ret;
|
484
|
-
}
|
485
|
-
|
486
|
-
size_t malloc_usable_size(void *p)
|
487
|
-
{
|
488
|
-
return ptr2hdr(p)->size;
|
489
|
-
}
|
490
|
-
|
491
|
-
static void
|
492
|
-
free_hdr_rcu(struct rcu_head *dead)
|
493
|
-
{
|
494
|
-
struct alloc_hdr *h = caa_container_of(dead, struct alloc_hdr, as.dead);
|
495
|
-
real_free(h->real);
|
496
|
-
}
|
497
|
-
|
498
|
-
void free(void *p)
|
499
|
-
{
|
500
|
-
if (p) {
|
501
|
-
struct alloc_hdr *h = ptr2hdr(p);
|
502
|
-
struct src_loc *l = h->as.live.loc;
|
503
|
-
|
504
|
-
if (!real_free) return; /* oh well, leak a little */
|
505
|
-
if (l && l != IS_HEAP_PAGE_BODY) {
|
506
|
-
size_t age = generation - h->as.live.gen;
|
507
|
-
|
508
|
-
uatomic_add(&total_bytes_dec, h->size);
|
509
|
-
uatomic_set(&h->size, 0);
|
510
|
-
uatomic_add(&l->frees, 1);
|
511
|
-
uatomic_add(&l->age_total, age);
|
512
|
-
|
513
|
-
mutex_lock(l->mtx);
|
514
|
-
cds_list_del_rcu(&h->anode);
|
515
|
-
if (age > l->max_lifespan)
|
516
|
-
l->max_lifespan = age;
|
517
|
-
mutex_unlock(l->mtx);
|
518
|
-
|
519
|
-
call_rcu(&h->as.dead, free_hdr_rcu);
|
520
|
-
} else if (l == IS_HEAP_PAGE_BODY) {
|
521
|
-
size_t gen = generation;
|
522
|
-
size_t age = gen - h->as.live.gen;
|
523
|
-
|
524
|
-
h->as.hpb_freed.at = gen;
|
525
|
-
|
526
|
-
mutex_lock(&hpb_stats.lock);
|
527
|
-
acc_add(&hpb_stats.alive, age);
|
528
|
-
|
529
|
-
/* hpb_stats.bodies => hpb_stats.freed */
|
530
|
-
cds_list_move(&h->anode, &hpb_stats.freed);
|
531
|
-
|
532
|
-
mutex_unlock(&hpb_stats.lock);
|
533
|
-
} else {
|
534
|
-
real_free(h->real);
|
535
|
-
}
|
536
|
-
}
|
537
|
-
}
|
538
|
-
|
539
|
-
static void
|
540
|
-
alloc_insert_rcu(struct src_loc *l, struct alloc_hdr *h, size_t size, void *real)
|
541
|
-
{
|
542
|
-
/* we need src_loc to remain alive for the duration of this call */
|
543
|
-
if (!h) return;
|
544
|
-
h->size = size;
|
545
|
-
h->real = real;
|
546
|
-
h->as.live.loc = l;
|
547
|
-
h->as.live.gen = generation;
|
548
|
-
if (l) {
|
549
|
-
mutex_lock(l->mtx);
|
550
|
-
cds_list_add_rcu(&h->anode, &l->allocs);
|
551
|
-
mutex_unlock(l->mtx);
|
552
|
-
}
|
553
|
-
}
|
554
|
-
|
555
|
-
static size_t size_align(size_t size, size_t alignment)
|
556
|
-
{
|
557
|
-
return ((size + (alignment - 1)) & ~(alignment - 1));
|
558
|
-
}
|
559
|
-
|
560
|
-
static bool ptr_is_aligned(const void *ptr, size_t alignment)
|
561
|
-
{
|
562
|
-
return ((uintptr_t)ptr & (alignment - 1)) == 0;
|
563
|
-
}
|
564
|
-
|
565
|
-
static void *ptr_align(void *ptr, size_t alignment)
|
566
|
-
{
|
567
|
-
return (void *)(((uintptr_t)ptr + (alignment - 1)) & ~(alignment - 1));
|
568
|
-
}
|
569
|
-
|
570
|
-
static bool is_power_of_two(size_t n) { return (n & (n - 1)) == 0; }
|
571
|
-
|
572
|
-
static int
|
573
|
-
internal_memalign(void **pp, size_t alignment, size_t size, uintptr_t caller)
|
574
|
-
{
|
575
|
-
struct src_loc *l;
|
576
|
-
struct alloc_hdr *h;
|
577
|
-
void *real;
|
578
|
-
size_t asize;
|
579
|
-
size_t d = alignment / sizeof(void*);
|
580
|
-
size_t r = alignment % sizeof(void*);
|
581
|
-
|
582
|
-
if (!real_malloc) return ENOMEM;
|
583
|
-
|
584
|
-
if (r != 0 || d == 0 || !is_power_of_two(d))
|
585
|
-
return EINVAL;
|
586
|
-
|
587
|
-
if (alignment <= ASSUMED_MALLOC_ALIGNMENT) {
|
588
|
-
void *p = malloc(size);
|
589
|
-
if (!p) return ENOMEM;
|
590
|
-
*pp = p;
|
591
|
-
return 0;
|
592
|
-
}
|
593
|
-
for (; alignment < sizeof(struct alloc_hdr); alignment *= 2)
|
594
|
-
; /* double alignment until >= sizeof(struct alloc_hdr) */
|
595
|
-
if (__builtin_add_overflow(size, alignment, &asize) ||
|
596
|
-
__builtin_add_overflow(asize, sizeof(struct alloc_hdr), &asize))
|
597
|
-
return ENOMEM;
|
598
|
-
|
599
|
-
|
600
|
-
if (alignment == HEAP_PAGE_ALIGN && size == HEAP_PAGE_SIZE) {
|
601
|
-
if (has_ec_p()) generation = rb_gc_count();
|
602
|
-
l = IS_HEAP_PAGE_BODY;
|
603
|
-
} else {
|
604
|
-
l = update_stats_rcu_lock(size, caller);
|
605
|
-
}
|
606
|
-
|
607
|
-
if (l == IS_HEAP_PAGE_BODY) {
|
608
|
-
void *p;
|
609
|
-
size_t gen = generation;
|
610
|
-
|
611
|
-
mutex_lock(&hpb_stats.lock);
|
612
|
-
|
613
|
-
/* reuse existing entry */
|
614
|
-
if (!cds_list_empty(&hpb_stats.freed)) {
|
615
|
-
size_t deathspan;
|
616
|
-
|
617
|
-
h = cds_list_first_entry(&hpb_stats.freed,
|
618
|
-
struct alloc_hdr, anode);
|
619
|
-
/* hpb_stats.freed => hpb_stats.bodies */
|
620
|
-
cds_list_move(&h->anode, &hpb_stats.bodies);
|
621
|
-
assert(h->size == size);
|
622
|
-
assert(h->real);
|
623
|
-
real = h->real;
|
624
|
-
p = hdr2ptr(h);
|
625
|
-
assert(ptr_is_aligned(p, alignment));
|
626
|
-
|
627
|
-
deathspan = gen - h->as.hpb_freed.at;
|
628
|
-
acc_add(&hpb_stats.reborn, deathspan);
|
629
|
-
}
|
630
|
-
else {
|
631
|
-
real = real_malloc(asize);
|
632
|
-
if (!real) return ENOMEM;
|
633
|
-
|
634
|
-
p = hdr2ptr(real);
|
635
|
-
if (!ptr_is_aligned(p, alignment))
|
636
|
-
p = ptr_align(p, alignment);
|
637
|
-
h = ptr2hdr(p);
|
638
|
-
h->size = size;
|
639
|
-
h->real = real;
|
640
|
-
cds_list_add(&h->anode, &hpb_stats.bodies);
|
641
|
-
}
|
642
|
-
mutex_unlock(&hpb_stats.lock);
|
643
|
-
h->as.live.loc = l;
|
644
|
-
h->as.live.gen = gen;
|
645
|
-
*pp = p;
|
646
|
-
}
|
647
|
-
else {
|
648
|
-
real = real_malloc(asize);
|
649
|
-
if (real) {
|
650
|
-
void *p = hdr2ptr(real);
|
651
|
-
if (!ptr_is_aligned(p, alignment))
|
652
|
-
p = ptr_align(p, alignment);
|
653
|
-
h = ptr2hdr(p);
|
654
|
-
alloc_insert_rcu(l, h, size, real);
|
655
|
-
*pp = p;
|
656
|
-
}
|
657
|
-
update_stats_rcu_unlock(l);
|
658
|
-
}
|
659
|
-
|
660
|
-
return real ? 0 : ENOMEM;
|
661
|
-
}
|
662
|
-
|
663
|
-
static void *
|
664
|
-
memalign_result(int err, void *p)
|
665
|
-
{
|
666
|
-
if (caa_unlikely(err))
|
667
|
-
errno = err;
|
668
|
-
return p;
|
669
|
-
}
|
670
|
-
|
671
|
-
void *memalign(size_t alignment, size_t size)
|
672
|
-
{
|
673
|
-
void *p = NULL;
|
674
|
-
int err = internal_memalign(&p, alignment, size, RETURN_ADDRESS(0));
|
675
|
-
return memalign_result(err, p);
|
676
|
-
}
|
677
|
-
|
678
|
-
int posix_memalign(void **p, size_t alignment, size_t size)
|
679
|
-
{
|
680
|
-
return internal_memalign(p, alignment, size, RETURN_ADDRESS(0));
|
681
|
-
}
|
682
|
-
|
683
|
-
void *aligned_alloc(size_t, size_t) __attribute__((alias("memalign")));
|
684
|
-
void cfree(void *) __attribute__((alias("free")));
|
685
|
-
|
686
|
-
void *valloc(size_t size)
|
687
|
-
{
|
688
|
-
void *p = NULL;
|
689
|
-
int err = internal_memalign(&p, page_size, size, RETURN_ADDRESS(0));
|
690
|
-
return memalign_result(err, p);
|
691
|
-
}
|
692
|
-
|
693
|
-
#if __GNUC__ < 7
|
694
|
-
# define add_overflow_p(a,b) __extension__({ \
|
695
|
-
__typeof__(a) _c; \
|
696
|
-
__builtin_add_overflow(a,b,&_c); \
|
697
|
-
})
|
698
|
-
#else
|
699
|
-
# define add_overflow_p(a,b) \
|
700
|
-
__builtin_add_overflow_p((a),(b),(__typeof__(a+b))0)
|
701
|
-
#endif
|
702
|
-
|
703
|
-
void *pvalloc(size_t size)
|
704
|
-
{
|
705
|
-
size_t alignment = page_size;
|
706
|
-
void *p = NULL;
|
707
|
-
int err;
|
708
|
-
|
709
|
-
if (add_overflow_p(size, alignment)) {
|
710
|
-
errno = ENOMEM;
|
711
|
-
return 0;
|
712
|
-
}
|
713
|
-
size = size_align(size, alignment);
|
714
|
-
err = internal_memalign(&p, alignment, size, RETURN_ADDRESS(0));
|
715
|
-
return memalign_result(err, p);
|
716
|
-
}
|
717
|
-
|
718
|
-
void *malloc(size_t size)
|
719
|
-
{
|
720
|
-
struct src_loc *l;
|
721
|
-
struct alloc_hdr *h;
|
722
|
-
size_t asize;
|
723
|
-
void *p;
|
724
|
-
|
725
|
-
if (__builtin_add_overflow(size, sizeof(struct alloc_hdr), &asize))
|
726
|
-
goto enomem;
|
727
|
-
|
728
|
-
/*
|
729
|
-
* Needed for C++ global declarations using "new",
|
730
|
-
* which happens before our constructor
|
731
|
-
*/
|
732
|
-
#ifndef __FreeBSD__
|
733
|
-
if (!real_malloc) {
|
734
|
-
if (resolving_malloc) goto enomem;
|
735
|
-
resolving_malloc = 1;
|
736
|
-
real_malloc = dlsym(RTLD_NEXT, "malloc");
|
737
|
-
}
|
738
|
-
#endif
|
739
|
-
l = update_stats_rcu_lock(size, RETURN_ADDRESS(0));
|
740
|
-
p = h = real_malloc(asize);
|
741
|
-
if (h) {
|
742
|
-
alloc_insert_rcu(l, h, size, h);
|
743
|
-
p = hdr2ptr(h);
|
744
|
-
}
|
745
|
-
update_stats_rcu_unlock(l);
|
746
|
-
if (caa_unlikely(!p)) errno = ENOMEM;
|
747
|
-
return p;
|
748
|
-
enomem:
|
749
|
-
errno = ENOMEM;
|
750
|
-
return 0;
|
751
|
-
}
|
752
|
-
|
753
|
-
void *calloc(size_t nmemb, size_t size)
|
754
|
-
{
|
755
|
-
void *p;
|
756
|
-
struct src_loc *l;
|
757
|
-
struct alloc_hdr *h;
|
758
|
-
size_t asize;
|
759
|
-
|
760
|
-
if (__builtin_mul_overflow(size, nmemb, &size)) {
|
761
|
-
errno = ENOMEM;
|
762
|
-
return 0;
|
763
|
-
}
|
764
|
-
if (__builtin_add_overflow(size, sizeof(struct alloc_hdr), &asize)) {
|
765
|
-
errno = ENOMEM;
|
766
|
-
return 0;
|
767
|
-
}
|
768
|
-
RETURN_IF_NOT_READY();
|
769
|
-
l = update_stats_rcu_lock(size, RETURN_ADDRESS(0));
|
770
|
-
p = h = real_malloc(asize);
|
771
|
-
if (p) {
|
772
|
-
alloc_insert_rcu(l, h, size, h);
|
773
|
-
p = hdr2ptr(h);
|
774
|
-
memset(p, 0, size);
|
775
|
-
}
|
776
|
-
update_stats_rcu_unlock(l);
|
777
|
-
if (caa_unlikely(!p)) errno = ENOMEM;
|
778
|
-
return p;
|
779
|
-
}
|
780
|
-
|
781
|
-
void *realloc(void *ptr, size_t size)
|
782
|
-
{
|
783
|
-
void *p;
|
784
|
-
struct src_loc *l;
|
785
|
-
struct alloc_hdr *h;
|
786
|
-
size_t asize;
|
787
|
-
|
788
|
-
if (!size) {
|
789
|
-
free(ptr);
|
790
|
-
return 0;
|
791
|
-
}
|
792
|
-
if (__builtin_add_overflow(size, sizeof(struct alloc_hdr), &asize)) {
|
793
|
-
errno = ENOMEM;
|
794
|
-
return 0;
|
795
|
-
}
|
796
|
-
RETURN_IF_NOT_READY();
|
797
|
-
|
798
|
-
l = update_stats_rcu_lock(size, RETURN_ADDRESS(0));
|
799
|
-
p = h = real_malloc(asize);
|
800
|
-
if (p) {
|
801
|
-
alloc_insert_rcu(l, h, size, h);
|
802
|
-
p = hdr2ptr(h);
|
803
|
-
}
|
804
|
-
update_stats_rcu_unlock(l);
|
805
|
-
|
806
|
-
if (ptr && p) {
|
807
|
-
struct alloc_hdr *old = ptr2hdr(ptr);
|
808
|
-
memcpy(p, ptr, old->size < size ? old->size : size);
|
809
|
-
free(ptr);
|
810
|
-
}
|
811
|
-
if (caa_unlikely(!p)) errno = ENOMEM;
|
812
|
-
return p;
|
813
|
-
}
|
814
|
-
|
815
|
-
struct dump_arg {
|
816
|
-
FILE *fp;
|
817
|
-
size_t min;
|
818
|
-
};
|
819
|
-
|
820
|
-
static void *dump_to_file(void *x)
|
821
|
-
{
|
822
|
-
struct dump_arg *a = x;
|
823
|
-
struct cds_lfht_iter iter;
|
824
|
-
struct src_loc *l;
|
825
|
-
struct cds_lfht *t;
|
826
|
-
|
827
|
-
++locating;
|
828
|
-
rcu_read_lock();
|
829
|
-
t = CMM_LOAD_SHARED(totals);
|
830
|
-
if (!t)
|
831
|
-
goto out_unlock;
|
832
|
-
cds_lfht_for_each_entry(t, &iter, l, hnode) {
|
833
|
-
const void *p = l->k;
|
834
|
-
char **s = 0;
|
835
|
-
if (l->total <= a->min) continue;
|
836
|
-
|
837
|
-
if (loc_is_addr(l)) {
|
838
|
-
s = backtrace_symbols(p, 1);
|
839
|
-
p = s[0];
|
840
|
-
}
|
841
|
-
fprintf(a->fp, "%16zu %12zu %s\n",
|
842
|
-
l->total, l->allocations, (const char *)p);
|
843
|
-
if (s) free(s);
|
844
|
-
}
|
845
|
-
out_unlock:
|
846
|
-
rcu_read_unlock();
|
847
|
-
--locating;
|
848
|
-
return 0;
|
849
|
-
}
|
850
13
|
|
851
14
|
/*
|
852
15
|
* call-seq:
|
@@ -861,7 +24,7 @@ out_unlock:
|
|
861
24
|
*
|
862
25
|
* total_size call_count location
|
863
26
|
*/
|
864
|
-
static VALUE mwrap_dump(int argc, VALUE *
|
27
|
+
static VALUE mwrap_dump(int argc, VALUE *argv, VALUE mod)
|
865
28
|
{
|
866
29
|
VALUE io, min;
|
867
30
|
struct dump_arg a;
|
@@ -871,7 +34,7 @@ static VALUE mwrap_dump(int argc, VALUE * argv, VALUE mod)
|
|
871
34
|
|
872
35
|
if (NIL_P(io))
|
873
36
|
/* library may be linked w/o Ruby */
|
874
|
-
io =
|
37
|
+
io = rb_stderr;
|
875
38
|
|
876
39
|
a.min = NIL_P(min) ? 0 : NUM2SIZET(min);
|
877
40
|
io = rb_io_get_io(io);
|
@@ -879,7 +42,7 @@ static VALUE mwrap_dump(int argc, VALUE * argv, VALUE mod)
|
|
879
42
|
GetOpenFile(io, fptr);
|
880
43
|
a.fp = rb_io_stdio_file(fptr);
|
881
44
|
|
882
|
-
rb_thread_call_without_gvl(dump_to_file, &a, 0, 0);
|
45
|
+
rb_thread_call_without_gvl((void *(*)(void *))dump_to_file, &a, 0, 0);
|
883
46
|
RB_GC_GUARD(io);
|
884
47
|
return Qnil;
|
885
48
|
}
|
@@ -887,24 +50,8 @@ static VALUE mwrap_dump(int argc, VALUE * argv, VALUE mod)
|
|
887
50
|
/* The whole operation is not remotely atomic... */
|
888
51
|
static void *totals_reset(void *ign)
|
889
52
|
{
|
890
|
-
|
891
|
-
|
892
|
-
struct src_loc *l;
|
893
|
-
|
894
|
-
uatomic_set(&total_bytes_inc, 0);
|
895
|
-
uatomic_set(&total_bytes_dec, 0);
|
896
|
-
|
897
|
-
rcu_read_lock();
|
898
|
-
t = CMM_LOAD_SHARED(totals);
|
899
|
-
cds_lfht_for_each_entry(t, &iter, l, hnode) {
|
900
|
-
uatomic_set(&l->total, 0);
|
901
|
-
uatomic_set(&l->allocations, 0);
|
902
|
-
uatomic_set(&l->frees, 0);
|
903
|
-
uatomic_set(&l->age_total, 0);
|
904
|
-
uatomic_set(&l->max_lifespan, 0);
|
905
|
-
}
|
906
|
-
rcu_read_unlock();
|
907
|
-
return 0;
|
53
|
+
mwrap_reset();
|
54
|
+
return NULL;
|
908
55
|
}
|
909
56
|
|
910
57
|
/*
|
@@ -916,18 +63,12 @@ static void *totals_reset(void *ign)
|
|
916
63
|
* This resets all statistics. This is not an atomic operation
|
917
64
|
* as other threads (outside of GVL) may increment counters.
|
918
65
|
*/
|
919
|
-
static VALUE
|
66
|
+
static VALUE reset_m(VALUE mod)
|
920
67
|
{
|
921
68
|
rb_thread_call_without_gvl(totals_reset, 0, 0, 0);
|
922
69
|
return Qnil;
|
923
70
|
}
|
924
71
|
|
925
|
-
/* :nodoc: */
|
926
|
-
static VALUE mwrap_clear(VALUE mod)
|
927
|
-
{
|
928
|
-
return mwrap_reset(mod);
|
929
|
-
}
|
930
|
-
|
931
72
|
static VALUE rcu_unlock_ensure(VALUE ignored)
|
932
73
|
{
|
933
74
|
rcu_read_unlock();
|
@@ -935,21 +76,31 @@ static VALUE rcu_unlock_ensure(VALUE ignored)
|
|
935
76
|
return Qfalse;
|
936
77
|
}
|
937
78
|
|
938
|
-
static VALUE location_string(struct src_loc *l)
|
79
|
+
static VALUE location_string(const struct src_loc *l)
|
939
80
|
{
|
940
|
-
VALUE
|
81
|
+
VALUE tmp = rb_str_new(NULL, 0);
|
941
82
|
|
942
|
-
if (
|
943
|
-
|
944
|
-
|
945
|
-
|
83
|
+
if (l->f) {
|
84
|
+
rb_str_cat(tmp, l->f->fn, l->f->fn_len);
|
85
|
+
if (l->lineno == U24_MAX)
|
86
|
+
rb_str_cat_cstr(tmp, ":-");
|
87
|
+
else
|
88
|
+
rb_str_catf(tmp, ":%u", l->lineno);
|
946
89
|
}
|
947
|
-
|
948
|
-
|
90
|
+
if (l->bt_len) {
|
91
|
+
AUTO_FREE char **s = bt_syms(l->bt, l->bt_len);
|
92
|
+
|
93
|
+
if (s) {
|
94
|
+
if (l->f)
|
95
|
+
rb_str_cat_cstr(tmp, "\n");
|
96
|
+
rb_str_cat_cstr(tmp, s[0]);
|
97
|
+
for (uint32_t i = 1; i < l->bt_len; ++i)
|
98
|
+
rb_str_catf(tmp, "\n%s", s[i]);
|
99
|
+
}
|
949
100
|
}
|
950
101
|
|
951
102
|
/* deduplicate and try to free up some memory */
|
952
|
-
ret = rb_funcall(tmp, id_uminus, 0);
|
103
|
+
VALUE ret = rb_funcall(tmp, id_uminus, 0);
|
953
104
|
if (!OBJ_FROZEN_RAW(tmp))
|
954
105
|
rb_str_resize(tmp, 0);
|
955
106
|
|
@@ -1021,17 +172,6 @@ static const rb_data_type_t src_loc_type = {
|
|
1021
172
|
|
1022
173
|
static VALUE cSrcLoc;
|
1023
174
|
|
1024
|
-
static int
|
1025
|
-
extract_addr(const char *str, size_t len, void **p)
|
1026
|
-
{
|
1027
|
-
const char *c;
|
1028
|
-
#if defined(__GLIBC__)
|
1029
|
-
return ((c = memrchr(str, '[', len)) && sscanf(c, "[%p]", p));
|
1030
|
-
#else /* tested FreeBSD */
|
1031
|
-
return ((c = strstr(str, "0x")) && sscanf(c, "%p", p));
|
1032
|
-
#endif
|
1033
|
-
}
|
1034
|
-
|
1035
175
|
/*
|
1036
176
|
* call-seq:
|
1037
177
|
* Mwrap[location] -> Mwrap::SourceLocation
|
@@ -1044,41 +184,11 @@ extract_addr(const char *str, size_t len, void **p)
|
|
1044
184
|
static VALUE mwrap_aref(VALUE mod, VALUE loc)
|
1045
185
|
{
|
1046
186
|
const char *str = StringValueCStr(loc);
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
struct cds_lfht_iter iter;
|
1051
|
-
struct cds_lfht_node *cur;
|
1052
|
-
struct cds_lfht *t;
|
1053
|
-
struct src_loc *l;
|
1054
|
-
VALUE val = Qnil;
|
1055
|
-
|
1056
|
-
if (extract_addr(str, len, (void **)&p)) {
|
1057
|
-
k = (void *)kbuf;
|
1058
|
-
memcpy(k->k, &p, sizeof(p));
|
1059
|
-
k->capa = 0;
|
1060
|
-
k->hval = jhash(k->k, sizeof(p), 0xdeadbeef);
|
1061
|
-
} else {
|
1062
|
-
k = (void *)kbuf;
|
1063
|
-
memcpy(k->k, str, len + 1);
|
1064
|
-
k->capa = len + 1;
|
1065
|
-
k->hval = jhash(k->k, k->capa, 0xdeadbeef);
|
1066
|
-
}
|
187
|
+
long len = RSTRING_LEN(loc);
|
188
|
+
assert(len >= 0);
|
189
|
+
struct src_loc *l = mwrap_get(str, (size_t)len);
|
1067
190
|
|
1068
|
-
|
1069
|
-
|
1070
|
-
t = CMM_LOAD_SHARED(totals);
|
1071
|
-
if (!t) return val;
|
1072
|
-
rcu_read_lock();
|
1073
|
-
|
1074
|
-
cds_lfht_lookup(t, k->hval, loc_eq, k, &iter);
|
1075
|
-
cur = cds_lfht_iter_get_node(&iter);
|
1076
|
-
if (cur) {
|
1077
|
-
l = caa_container_of(cur, struct src_loc, hnode);
|
1078
|
-
val = TypedData_Wrap_Struct(cSrcLoc, &src_loc_type, l);
|
1079
|
-
}
|
1080
|
-
rcu_read_unlock();
|
1081
|
-
return val;
|
191
|
+
return l ? TypedData_Wrap_Struct(cSrcLoc, &src_loc_type, l) : Qnil;
|
1082
192
|
}
|
1083
193
|
|
1084
194
|
static VALUE src_loc_each_i(VALUE p)
|
@@ -1102,7 +212,7 @@ static VALUE src_loc_each_i(VALUE p)
|
|
1102
212
|
return Qfalse;
|
1103
213
|
}
|
1104
214
|
|
1105
|
-
static struct src_loc *
|
215
|
+
static struct src_loc *src_loc_of(VALUE self)
|
1106
216
|
{
|
1107
217
|
struct src_loc *l;
|
1108
218
|
TypedData_Get_Struct(self, struct src_loc, &src_loc_type, l);
|
@@ -1124,7 +234,7 @@ static struct src_loc *src_loc_get(VALUE self)
|
|
1124
234
|
*/
|
1125
235
|
static VALUE src_loc_each(VALUE self)
|
1126
236
|
{
|
1127
|
-
struct src_loc *l =
|
237
|
+
struct src_loc *l = src_loc_of(self);
|
1128
238
|
|
1129
239
|
assert(locating == 0 && "forgot to clear locating");
|
1130
240
|
++locating;
|
@@ -1139,7 +249,7 @@ static VALUE src_loc_each(VALUE self)
|
|
1139
249
|
*/
|
1140
250
|
static VALUE src_loc_mean_lifespan(VALUE self)
|
1141
251
|
{
|
1142
|
-
struct src_loc *l =
|
252
|
+
struct src_loc *l = src_loc_of(self);
|
1143
253
|
size_t tot, frees;
|
1144
254
|
|
1145
255
|
frees = uatomic_read(&l->frees);
|
@@ -1150,19 +260,19 @@ static VALUE src_loc_mean_lifespan(VALUE self)
|
|
1150
260
|
/* The number of frees made from this location */
|
1151
261
|
static VALUE src_loc_frees(VALUE self)
|
1152
262
|
{
|
1153
|
-
return SIZET2NUM(uatomic_read(&
|
263
|
+
return SIZET2NUM(uatomic_read(&src_loc_of(self)->frees));
|
1154
264
|
}
|
1155
265
|
|
1156
266
|
/* The number of allocations made from this location */
|
1157
267
|
static VALUE src_loc_allocations(VALUE self)
|
1158
268
|
{
|
1159
|
-
return SIZET2NUM(uatomic_read(&
|
269
|
+
return SIZET2NUM(uatomic_read(&src_loc_of(self)->allocations));
|
1160
270
|
}
|
1161
271
|
|
1162
272
|
/* The total number of bytes allocated from this location */
|
1163
273
|
static VALUE src_loc_total(VALUE self)
|
1164
274
|
{
|
1165
|
-
return SIZET2NUM(uatomic_read(&
|
275
|
+
return SIZET2NUM(uatomic_read(&src_loc_of(self)->total));
|
1166
276
|
}
|
1167
277
|
|
1168
278
|
/*
|
@@ -1171,7 +281,7 @@ static VALUE src_loc_total(VALUE self)
|
|
1171
281
|
*/
|
1172
282
|
static VALUE src_loc_max_lifespan(VALUE self)
|
1173
283
|
{
|
1174
|
-
return SIZET2NUM(uatomic_read(&
|
284
|
+
return SIZET2NUM(uatomic_read(&src_loc_of(self)->max_lifespan));
|
1175
285
|
}
|
1176
286
|
|
1177
287
|
/*
|
@@ -1179,7 +289,7 @@ static VALUE src_loc_max_lifespan(VALUE self)
|
|
1179
289
|
*/
|
1180
290
|
static VALUE src_loc_name(VALUE self)
|
1181
291
|
{
|
1182
|
-
struct src_loc *l =
|
292
|
+
struct src_loc *l = src_loc_of(self);
|
1183
293
|
VALUE ret;
|
1184
294
|
|
1185
295
|
++locating;
|
@@ -1226,73 +336,6 @@ static VALUE total_dec(VALUE mod)
|
|
1226
336
|
return SIZET2NUM(total_bytes_dec);
|
1227
337
|
}
|
1228
338
|
|
1229
|
-
static VALUE hpb_each_yield(VALUE ignore)
|
1230
|
-
{
|
1231
|
-
struct alloc_hdr *h, *next;
|
1232
|
-
|
1233
|
-
cds_list_for_each_entry_safe(h, next, &hpb_stats.bodies, anode) {
|
1234
|
-
VALUE v[2]; /* [ generation, address ] */
|
1235
|
-
void *addr = hdr2ptr(h);
|
1236
|
-
assert(ptr_is_aligned(addr, HEAP_PAGE_ALIGN));
|
1237
|
-
v[0] = LONG2NUM((long)addr);
|
1238
|
-
v[1] = SIZET2NUM(h->as.live.gen);
|
1239
|
-
rb_yield_values2(2, v);
|
1240
|
-
}
|
1241
|
-
return Qnil;
|
1242
|
-
}
|
1243
|
-
|
1244
|
-
/*
|
1245
|
-
* call-seq:
|
1246
|
-
*
|
1247
|
-
* Mwrap::HeapPageBody.each { |gen, addr| } -> Integer
|
1248
|
-
*
|
1249
|
-
* Yields the generation (GC.count) the heap page body was created
|
1250
|
-
* and address of the heap page body as an Integer. Returns the
|
1251
|
-
* number of allocated pages as an Integer. This return value should
|
1252
|
-
* match the result of GC.stat(:heap_allocated_pages)
|
1253
|
-
*/
|
1254
|
-
static VALUE hpb_each(VALUE mod)
|
1255
|
-
{
|
1256
|
-
++locating;
|
1257
|
-
return rb_ensure(hpb_each_yield, Qfalse, reset_locating, 0);
|
1258
|
-
}
|
1259
|
-
|
1260
|
-
/*
|
1261
|
-
* call-seq:
|
1262
|
-
*
|
1263
|
-
* Mwrap::HeapPageBody.stat -> Hash
|
1264
|
-
* Mwrap::HeapPageBody.stat(hash) -> hash
|
1265
|
-
*
|
1266
|
-
* The maximum lifespan of a heap page body in the Ruby VM.
|
1267
|
-
* This may be Infinity if no heap page bodies were ever freed.
|
1268
|
-
*/
|
1269
|
-
static VALUE hpb_stat(int argc, VALUE *argv, VALUE hpb)
|
1270
|
-
{
|
1271
|
-
VALUE h;
|
1272
|
-
|
1273
|
-
rb_scan_args(argc, argv, "01", &h);
|
1274
|
-
if (NIL_P(h))
|
1275
|
-
h = rb_hash_new();
|
1276
|
-
else if (!RB_TYPE_P(h, T_HASH))
|
1277
|
-
rb_raise(rb_eTypeError, "not a hash %+"PRIsVALUE, h);
|
1278
|
-
|
1279
|
-
++locating;
|
1280
|
-
#define S(x) ID2SYM(rb_intern(#x))
|
1281
|
-
rb_hash_aset(h, S(lifespan_max), acc_max(&hpb_stats.alive));
|
1282
|
-
rb_hash_aset(h, S(lifespan_min), acc_min(&hpb_stats.alive));
|
1283
|
-
rb_hash_aset(h, S(lifespan_mean), acc_mean(&hpb_stats.alive));
|
1284
|
-
rb_hash_aset(h, S(lifespan_stddev), acc_stddev(&hpb_stats.alive));
|
1285
|
-
rb_hash_aset(h, S(deathspan_max), acc_max(&hpb_stats.reborn));
|
1286
|
-
rb_hash_aset(h, S(deathspan_min), acc_min(&hpb_stats.reborn));
|
1287
|
-
rb_hash_aset(h, S(deathspan_mean), acc_mean(&hpb_stats.reborn));
|
1288
|
-
rb_hash_aset(h, S(deathspan_stddev), acc_stddev(&hpb_stats.reborn));
|
1289
|
-
rb_hash_aset(h, S(resurrects), SIZET2NUM(hpb_stats.reborn.nr));
|
1290
|
-
#undef S
|
1291
|
-
--locating;
|
1292
|
-
|
1293
|
-
return h;
|
1294
|
-
}
|
1295
|
-
|
1296
339
|
/*
|
1297
340
|
* Document-module: Mwrap
|
1298
341
|
*
|
@@ -1311,19 +354,13 @@ static VALUE hpb_stat(int argc, VALUE *argv, VALUE hpb)
|
|
1311
354
|
* * dump_fd: a writable FD to dump to
|
1312
355
|
* * dump_path: a path to dump to, the file is opened in O_APPEND mode
|
1313
356
|
* * dump_min: the minimum allocation size (total) to dump
|
1314
|
-
* * dump_heap: mask of heap_page_body statistics to dump
|
1315
357
|
*
|
1316
358
|
* If both `dump_fd' and `dump_path' are specified, dump_path takes
|
1317
359
|
* precedence.
|
1318
|
-
*
|
1319
|
-
* dump_heap bitmask
|
1320
|
-
* * 0x01 - summary stats (same info as HeapPageBody.stat)
|
1321
|
-
* * 0x02 - all live heaps (similar to HeapPageBody.each)
|
1322
|
-
* * 0x04 - skip non-heap_page_body-related output
|
1323
360
|
*/
|
1324
361
|
void Init_mwrap(void)
|
1325
362
|
{
|
1326
|
-
VALUE mod
|
363
|
+
VALUE mod;
|
1327
364
|
|
1328
365
|
++locating;
|
1329
366
|
mod = rb_define_module("Mwrap");
|
@@ -1337,9 +374,10 @@ void Init_mwrap(void)
|
|
1337
374
|
* This class is only available since mwrap 2.0.0+.
|
1338
375
|
*/
|
1339
376
|
cSrcLoc = rb_define_class_under(mod, "SourceLocation", rb_cObject);
|
377
|
+
rb_undef_alloc_func(cSrcLoc);
|
1340
378
|
rb_define_singleton_method(mod, "dump", mwrap_dump, -1);
|
1341
|
-
rb_define_singleton_method(mod, "reset",
|
1342
|
-
rb_define_singleton_method(mod, "clear",
|
379
|
+
rb_define_singleton_method(mod, "reset", reset_m, 0);
|
380
|
+
rb_define_singleton_method(mod, "clear", reset_m, 0);
|
1343
381
|
rb_define_singleton_method(mod, "each", mwrap_each, -1);
|
1344
382
|
rb_define_singleton_method(mod, "[]", mwrap_aref, 1);
|
1345
383
|
rb_define_singleton_method(mod, "quiet", mwrap_quiet, 0);
|
@@ -1355,137 +393,5 @@ void Init_mwrap(void)
|
|
1355
393
|
rb_define_method(cSrcLoc, "max_lifespan", src_loc_max_lifespan, 0);
|
1356
394
|
rb_define_method(cSrcLoc, "name", src_loc_name, 0);
|
1357
395
|
|
1358
|
-
/*
|
1359
|
-
* Information about "struct heap_page_body" allocations from
|
1360
|
-
* Ruby gc.c. This can be useful for tracking fragmentation
|
1361
|
-
* from posix_memalign(3) use in mainline Ruby:
|
1362
|
-
*
|
1363
|
-
* https://sourceware.org/bugzilla/show_bug.cgi?id=14581
|
1364
|
-
*
|
1365
|
-
* These statistics are never reset by Mwrap.reset or
|
1366
|
-
* any other method. They only make sense in the context
|
1367
|
-
* of an entire program lifetime.
|
1368
|
-
*/
|
1369
|
-
hpb = rb_define_class_under(mod, "HeapPageBody", rb_cObject);
|
1370
|
-
rb_define_singleton_method(hpb, "stat", hpb_stat, -1);
|
1371
|
-
rb_define_singleton_method(hpb, "each", hpb_each, 0);
|
1372
|
-
|
1373
|
-
--locating;
|
1374
|
-
}
|
1375
|
-
|
1376
|
-
enum {
|
1377
|
-
DUMP_HPB_STATS = 0x1,
|
1378
|
-
DUMP_HPB_EACH = 0x2,
|
1379
|
-
DUMP_HPB_EXCL = 0x4,
|
1380
|
-
};
|
1381
|
-
|
1382
|
-
static void dump_hpb(FILE *fp, unsigned flags)
|
1383
|
-
{
|
1384
|
-
if (flags & DUMP_HPB_STATS) {
|
1385
|
-
fprintf(fp,
|
1386
|
-
"lifespan_max: %"PRId64"\n"
|
1387
|
-
"lifespan_min:%s%"PRId64"\n"
|
1388
|
-
"lifespan_mean: %0.3f\n"
|
1389
|
-
"lifespan_stddev: %0.3f\n"
|
1390
|
-
"deathspan_max: %"PRId64"\n"
|
1391
|
-
"deathspan_min:%s%"PRId64"\n"
|
1392
|
-
"deathspan_mean: %0.3f\n"
|
1393
|
-
"deathspan_stddev: %0.3f\n"
|
1394
|
-
"gc_count: %zu\n",
|
1395
|
-
hpb_stats.alive.max,
|
1396
|
-
hpb_stats.alive.min == INT64_MAX ? " -" : " ",
|
1397
|
-
hpb_stats.alive.min,
|
1398
|
-
hpb_stats.alive.mean,
|
1399
|
-
acc_stddev_dbl(&hpb_stats.alive),
|
1400
|
-
hpb_stats.reborn.max,
|
1401
|
-
hpb_stats.reborn.min == INT64_MAX ? " -" : " ",
|
1402
|
-
hpb_stats.reborn.min,
|
1403
|
-
hpb_stats.reborn.mean,
|
1404
|
-
acc_stddev_dbl(&hpb_stats.reborn),
|
1405
|
-
/* n.b.: unsafe to call rb_gc_count() in destructor */
|
1406
|
-
generation);
|
1407
|
-
}
|
1408
|
-
if (flags & DUMP_HPB_EACH) {
|
1409
|
-
struct alloc_hdr *h;
|
1410
|
-
|
1411
|
-
cds_list_for_each_entry(h, &hpb_stats.bodies, anode) {
|
1412
|
-
void *addr = hdr2ptr(h);
|
1413
|
-
|
1414
|
-
fprintf(fp, "%p\t%zu\n", addr, h->as.live.gen);
|
1415
|
-
}
|
1416
|
-
}
|
1417
|
-
}
|
1418
|
-
|
1419
|
-
/* rb_cloexec_open isn't usable by non-Ruby processes */
|
1420
|
-
#ifndef O_CLOEXEC
|
1421
|
-
# define O_CLOEXEC 0
|
1422
|
-
#endif
|
1423
|
-
|
1424
|
-
__attribute__ ((destructor))
|
1425
|
-
static void mwrap_dump_destructor(void)
|
1426
|
-
{
|
1427
|
-
const char *opt = getenv("MWRAP");
|
1428
|
-
const char *modes[] = { "a", "a+", "w", "w+", "r+" };
|
1429
|
-
struct dump_arg a = { .min = 0 };
|
1430
|
-
size_t i;
|
1431
|
-
int dump_fd;
|
1432
|
-
unsigned dump_heap = 0;
|
1433
|
-
char *dump_path;
|
1434
|
-
char *s;
|
1435
|
-
|
1436
|
-
if (!opt)
|
1437
|
-
return;
|
1438
|
-
|
1439
|
-
++locating;
|
1440
|
-
if ((dump_path = strstr(opt, "dump_path:")) &&
|
1441
|
-
(dump_path += sizeof("dump_path")) &&
|
1442
|
-
*dump_path) {
|
1443
|
-
char *end = strchr(dump_path, ',');
|
1444
|
-
if (end) {
|
1445
|
-
char *tmp = alloca(end - dump_path + 1);
|
1446
|
-
end = mempcpy(tmp, dump_path, end - dump_path);
|
1447
|
-
*end = 0;
|
1448
|
-
dump_path = tmp;
|
1449
|
-
}
|
1450
|
-
dump_fd = open(dump_path, O_CLOEXEC|O_WRONLY|O_APPEND|O_CREAT,
|
1451
|
-
0666);
|
1452
|
-
if (dump_fd < 0) {
|
1453
|
-
fprintf(stderr, "open %s failed: %s\n", dump_path,
|
1454
|
-
strerror(errno));
|
1455
|
-
goto out;
|
1456
|
-
}
|
1457
|
-
}
|
1458
|
-
else if (!sscanf(opt, "dump_fd:%d", &dump_fd))
|
1459
|
-
goto out;
|
1460
|
-
|
1461
|
-
if ((s = strstr(opt, "dump_min:")))
|
1462
|
-
sscanf(s, "dump_min:%zu", &a.min);
|
1463
|
-
|
1464
|
-
if ((s = strstr(opt, "dump_heap:")))
|
1465
|
-
sscanf(s, "dump_heap:%u", &dump_heap);
|
1466
|
-
|
1467
|
-
switch (dump_fd) {
|
1468
|
-
case 0: goto out;
|
1469
|
-
case 1: a.fp = stdout; break;
|
1470
|
-
case 2: a.fp = stderr; break;
|
1471
|
-
default:
|
1472
|
-
if (dump_fd < 0)
|
1473
|
-
goto out;
|
1474
|
-
a.fp = 0;
|
1475
|
-
|
1476
|
-
for (i = 0; !a.fp && i < 5; i++)
|
1477
|
-
a.fp = fdopen(dump_fd, modes[i]);
|
1478
|
-
|
1479
|
-
if (!a.fp) {
|
1480
|
-
fprintf(stderr, "failed to open fd=%d: %s\n",
|
1481
|
-
dump_fd, strerror(errno));
|
1482
|
-
goto out;
|
1483
|
-
}
|
1484
|
-
/* we'll leak some memory here, but this is a destructor */
|
1485
|
-
}
|
1486
|
-
if ((dump_heap & DUMP_HPB_EXCL) == 0)
|
1487
|
-
dump_to_file(&a);
|
1488
|
-
dump_hpb(a.fp, dump_heap);
|
1489
|
-
out:
|
1490
396
|
--locating;
|
1491
397
|
}
|