raindrops 0.16.0 → 0.20.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.document +1 -1
- data/.manifest +3 -2
- data/.olddoc.yml +14 -7
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +1 -1
- data/LATEST +6 -24
- data/LICENSE +3 -3
- data/NEWS +109 -0
- data/README +20 -21
- data/TODO +1 -0
- data/examples/linux-listener-stats.rb +1 -2
- data/examples/watcher_demo.ru +1 -1
- data/examples/yahns.conf.rb +30 -0
- data/examples/zbatery.conf.rb +4 -1
- data/ext/raindrops/extconf.rb +106 -2
- data/ext/raindrops/linux_inet_diag.c +60 -85
- data/ext/raindrops/raindrops.c +75 -21
- data/ext/raindrops/tcp_info.c +245 -0
- data/lib/raindrops/aggregate/pmq.rb +23 -17
- data/lib/raindrops/aggregate.rb +1 -1
- data/lib/raindrops/linux.rb +4 -5
- data/lib/raindrops/middleware/proxy.rb +2 -2
- data/lib/raindrops/middleware.rb +2 -2
- data/lib/raindrops/watcher.rb +13 -13
- data/lib/raindrops.rb +25 -1
- data/pkg.mk +3 -2
- data/raindrops.gemspec +12 -16
- data/test/ipv6_enabled.rb +4 -4
- data/test/test_aggregate_pmq.rb +1 -1
- data/test/test_inet_diag_socket.rb +1 -1
- data/test/test_last_data_recv_unicorn.rb +1 -1
- data/test/test_linux.rb +3 -2
- data/test/test_linux_all_tcp_listen_stats_leak.rb +2 -2
- data/test/test_raindrops.rb +43 -1
- data/test/{test_linux_tcp_info.rb → test_tcp_info.rb} +34 -14
- data/test/test_watcher.rb +15 -10
- metadata +18 -56
- data/ext/raindrops/linux_tcp_info.c +0 -173
@@ -1,46 +1,24 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
#include <stdarg.h>
|
3
|
-
#
|
4
|
-
# include <ruby/st.h>
|
5
|
-
#else
|
6
|
-
# include <st.h>
|
7
|
-
#endif
|
3
|
+
#include <ruby/st.h>
|
8
4
|
#include "my_fileno.h"
|
9
5
|
#ifdef __linux__
|
10
6
|
|
11
|
-
/* Ruby 1.8.6+ macros (for compatibility with Ruby 1.9) */
|
12
|
-
#ifndef RSTRING_LEN
|
13
|
-
# define RSTRING_LEN(s) (RSTRING(s)->len)
|
14
|
-
#endif
|
15
|
-
|
16
|
-
/* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
|
17
|
-
#if !defined(HAVE_RB_THREAD_BLOCKING_REGION) && \
|
18
|
-
!defined(HAVE_RB_THREAD_IO_BLOCKING_REGION)
|
19
|
-
# include <rubysig.h>
|
20
|
-
# define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
|
21
|
-
typedef void rb_unblock_function_t(void *);
|
22
|
-
typedef VALUE rb_blocking_function_t(void *);
|
23
|
-
static VALUE
|
24
|
-
rb_thread_blocking_region(
|
25
|
-
rb_blocking_function_t *func, void *data1,
|
26
|
-
rb_unblock_function_t *ubf, void *data2)
|
27
|
-
{
|
28
|
-
VALUE rv;
|
29
|
-
|
30
|
-
TRAP_BEG;
|
31
|
-
rv = func(data1);
|
32
|
-
TRAP_END;
|
33
|
-
|
34
|
-
return rv;
|
35
|
-
}
|
36
|
-
#endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
|
37
|
-
|
38
7
|
#ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION
|
8
|
+
/* Ruby 1.9.3 and 2.0.0 */
|
39
9
|
VALUE rb_thread_io_blocking_region(rb_blocking_function_t *, void *, int);
|
10
|
+
# define rd_fd_region(fn,data,fd) \
|
11
|
+
rb_thread_io_blocking_region((fn),(data),(fd))
|
12
|
+
#elif defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && \
|
13
|
+
defined(HAVE_RUBY_THREAD_H) && HAVE_RUBY_THREAD_H
|
14
|
+
/* in case Ruby 2.0+ ever drops rb_thread_io_blocking_region: */
|
15
|
+
# include <ruby/thread.h>
|
16
|
+
# define COMPAT_FN (void *(*)(void *))
|
17
|
+
# define rd_fd_region(fn,data,fd) \
|
18
|
+
rb_thread_call_without_gvl(COMPAT_FN(fn),(data),RUBY_UBF_IO,NULL)
|
40
19
|
#else
|
41
|
-
#
|
42
|
-
|
43
|
-
#endif /* HAVE_RB_THREAD_IO_BLOCKING_REGION */
|
20
|
+
# error Ruby <= 1.8 not supported
|
21
|
+
#endif
|
44
22
|
|
45
23
|
#include <assert.h>
|
46
24
|
#include <errno.h>
|
@@ -214,6 +192,10 @@ static const char *addr_any(sa_family_t family)
|
|
214
192
|
return ipv6;
|
215
193
|
}
|
216
194
|
|
195
|
+
#ifdef __GNUC__
|
196
|
+
static void bug_warn_nogvl(const char *, ...)
|
197
|
+
__attribute__((format(printf,1,2)));
|
198
|
+
#endif
|
217
199
|
static void bug_warn_nogvl(const char *fmt, ...)
|
218
200
|
{
|
219
201
|
va_list ap;
|
@@ -223,86 +205,76 @@ static void bug_warn_nogvl(const char *fmt, ...)
|
|
223
205
|
va_end(ap);
|
224
206
|
|
225
207
|
fprintf(stderr, "Please report how you produced this at "\
|
226
|
-
"raindrops-public@
|
208
|
+
"raindrops-public@yhbt.net\n");
|
227
209
|
fflush(stderr);
|
228
210
|
}
|
229
211
|
|
230
212
|
static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
|
231
213
|
{
|
232
|
-
char *key, *port, *old_key;
|
214
|
+
char *host, *key, *port, *old_key;
|
233
215
|
size_t alloca_len;
|
234
216
|
struct listen_stats *stats;
|
235
|
-
socklen_t
|
217
|
+
socklen_t hostlen;
|
236
218
|
socklen_t portlen = (socklen_t)sizeof("65535");
|
237
|
-
|
238
|
-
|
239
|
-
int rc;
|
240
|
-
int flags = NI_NUMERICHOST | NI_NUMERICSERV;
|
219
|
+
int n;
|
220
|
+
const void *src = r->id.idiag_src;
|
241
221
|
|
242
|
-
switch (
|
222
|
+
switch (r->idiag_family) {
|
243
223
|
case AF_INET: {
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
alloca_len = keylen + 1 + portlen;
|
248
|
-
key = alloca(alloca_len);
|
249
|
-
key[keylen] = 0; /* will be ':' later */
|
250
|
-
port = key + keylen + 1;
|
251
|
-
rc = getnameinfo(&sa.sa, len,
|
252
|
-
key, keylen, port, portlen, flags);
|
224
|
+
hostlen = INET_ADDRSTRLEN;
|
225
|
+
alloca_len = hostlen + portlen;
|
226
|
+
host = key = alloca(alloca_len);
|
253
227
|
break;
|
254
228
|
}
|
255
229
|
case AF_INET6: {
|
256
|
-
|
257
|
-
|
258
|
-
keylen = INET6_ADDRSTRLEN;
|
259
|
-
/* [ ] */
|
260
|
-
alloca_len = 1 + keylen + 1 + 1 + portlen;
|
230
|
+
hostlen = INET6_ADDRSTRLEN;
|
231
|
+
alloca_len = 1 + hostlen + 1 + portlen;
|
261
232
|
key = alloca(alloca_len);
|
262
|
-
|
263
|
-
key[1 + keylen + 1] = 0; /* will be ':' later */
|
264
|
-
port = 1 + key + keylen + 1 + 1;
|
265
|
-
rc = getnameinfo(&sa.sa, len,
|
266
|
-
key + 1, keylen, port, portlen, flags);
|
233
|
+
host = key + 1;
|
267
234
|
break;
|
268
235
|
}
|
269
236
|
default:
|
270
237
|
assert(0 && "unsupported address family, could that be IPv7?!");
|
271
238
|
}
|
272
|
-
if (
|
273
|
-
bug_warn_nogvl("BUG:
|
274
|
-
*key = 0;
|
239
|
+
if (!inet_ntop(r->idiag_family, src, host, hostlen)) {
|
240
|
+
bug_warn_nogvl("BUG: inet_ntop: %s\n", strerror(errno));
|
241
|
+
*key = '\0';
|
242
|
+
*host = '\0';
|
275
243
|
}
|
276
|
-
|
277
|
-
|
278
|
-
portlen = (socklen_t)strlen(port);
|
279
|
-
|
280
|
-
switch (sa.ss.ss_family) {
|
244
|
+
hostlen = (socklen_t)strlen(host);
|
245
|
+
switch (r->idiag_family) {
|
281
246
|
case AF_INET:
|
282
|
-
|
283
|
-
|
247
|
+
host[hostlen] = ':';
|
248
|
+
port = host + hostlen + 1;
|
284
249
|
break;
|
285
250
|
case AF_INET6:
|
286
|
-
key[
|
287
|
-
|
288
|
-
|
289
|
-
|
251
|
+
key[0] = '[';
|
252
|
+
host[hostlen] = ']';
|
253
|
+
host[hostlen + 1] = ':';
|
254
|
+
port = host + hostlen + 2;
|
290
255
|
break;
|
291
256
|
default:
|
292
257
|
assert(0 && "unsupported address family, could that be IPv7?!");
|
293
258
|
}
|
294
259
|
|
260
|
+
n = snprintf(port, portlen, "%u", ntohs(r->id.idiag_sport));
|
261
|
+
if (n <= 0) {
|
262
|
+
bug_warn_nogvl("BUG: snprintf port: %d\n", n);
|
263
|
+
*key = '\0';
|
264
|
+
}
|
265
|
+
|
295
266
|
if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
|
296
267
|
return stats;
|
297
268
|
|
298
269
|
old_key = key;
|
299
270
|
|
300
271
|
if (r->idiag_state == TCP_ESTABLISHED) {
|
301
|
-
|
302
|
-
addr_any(
|
272
|
+
n = snprintf(key, alloca_len, "%s:%u",
|
273
|
+
addr_any(r->idiag_family),
|
303
274
|
ntohs(r->id.idiag_sport));
|
304
275
|
if (n <= 0) {
|
305
276
|
bug_warn_nogvl("BUG: snprintf: %d\n", n);
|
277
|
+
*key = '\0';
|
306
278
|
}
|
307
279
|
if (st_lookup(table, (st_data_t)key, (st_data_t *)&stats))
|
308
280
|
return stats;
|
@@ -315,8 +287,9 @@ static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
|
|
315
287
|
memcpy(key, old_key, n + 1);
|
316
288
|
}
|
317
289
|
} else {
|
318
|
-
|
319
|
-
|
290
|
+
size_t old_len = strlen(old_key) + 1;
|
291
|
+
key = xmalloc(old_len);
|
292
|
+
memcpy(key, old_key, old_len);
|
320
293
|
}
|
321
294
|
stats = xcalloc(1, sizeof(struct listen_stats));
|
322
295
|
st_insert(table, (st_data_t)key, (st_data_t)stats);
|
@@ -613,7 +586,7 @@ static VALUE tcp_stats(struct nogvl_args *args, VALUE addr)
|
|
613
586
|
gen_bytecode(&args->iov[2], &query_addr);
|
614
587
|
|
615
588
|
memset(&args->stats, 0, sizeof(struct listen_stats));
|
616
|
-
nl_errcheck(
|
589
|
+
nl_errcheck(rd_fd_region(diag, args, args->fd));
|
617
590
|
|
618
591
|
return rb_listen_stats(&args->stats);
|
619
592
|
}
|
@@ -690,7 +663,7 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
|
|
690
663
|
"addr must be an array of strings, a string, or nil");
|
691
664
|
}
|
692
665
|
|
693
|
-
nl_errcheck(
|
666
|
+
nl_errcheck(rd_fd_region(diag, &args, args.fd));
|
694
667
|
|
695
668
|
st_foreach(args.table, NIL_P(addrs) ? st_to_hash : st_AND_hash, rv);
|
696
669
|
st_free_table(args.table);
|
@@ -705,11 +678,12 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
|
|
705
678
|
|
706
679
|
void Init_raindrops_linux_inet_diag(void)
|
707
680
|
{
|
708
|
-
VALUE cRaindrops =
|
681
|
+
VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
|
709
682
|
VALUE mLinux = rb_define_module_under(cRaindrops, "Linux");
|
683
|
+
VALUE Socket;
|
710
684
|
|
711
685
|
rb_require("socket");
|
712
|
-
|
686
|
+
Socket = rb_const_get(rb_cObject, rb_intern("Socket"));
|
713
687
|
id_new = rb_intern("new");
|
714
688
|
|
715
689
|
/*
|
@@ -718,10 +692,11 @@ void Init_raindrops_linux_inet_diag(void)
|
|
718
692
|
* This is a subclass of +Socket+ specifically for talking
|
719
693
|
* to the inet_diag facility of Netlink.
|
720
694
|
*/
|
721
|
-
cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket",
|
695
|
+
cIDSock = rb_define_class_under(cRaindrops, "InetDiagSocket", Socket);
|
722
696
|
rb_define_singleton_method(cIDSock, "new", ids_s_new, 0);
|
723
697
|
|
724
698
|
cListenStats = rb_const_get(cRaindrops, rb_intern("ListenStats"));
|
699
|
+
rb_gc_register_mark_object(cListenStats); /* pin */
|
725
700
|
|
726
701
|
rb_define_module_function(mLinux, "tcp_listener_stats",
|
727
702
|
tcp_listener_stats, -1);
|
data/ext/raindrops/raindrops.c
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
#include <assert.h>
|
5
5
|
#include <errno.h>
|
6
6
|
#include <stddef.h>
|
7
|
+
#include <string.h>
|
7
8
|
#include "raindrops_atomic.h"
|
8
9
|
|
9
10
|
#ifndef SIZET2NUM
|
@@ -34,11 +35,19 @@ struct raindrops {
|
|
34
35
|
size_t size;
|
35
36
|
size_t capa;
|
36
37
|
pid_t pid;
|
38
|
+
VALUE io;
|
37
39
|
struct raindrop *drops;
|
38
40
|
};
|
39
41
|
|
40
42
|
/* called by GC */
|
41
|
-
static void
|
43
|
+
static void rd_mark(void *ptr)
|
44
|
+
{
|
45
|
+
struct raindrops *r = ptr;
|
46
|
+
rb_gc_mark(r->io);
|
47
|
+
}
|
48
|
+
|
49
|
+
/* called by GC */
|
50
|
+
static void rd_free(void *ptr)
|
42
51
|
{
|
43
52
|
struct raindrops *r = ptr;
|
44
53
|
|
@@ -51,11 +60,24 @@ static void gcfree(void *ptr)
|
|
51
60
|
xfree(ptr);
|
52
61
|
}
|
53
62
|
|
63
|
+
static size_t rd_memsize(const void *ptr)
|
64
|
+
{
|
65
|
+
const struct raindrops *r = ptr;
|
66
|
+
|
67
|
+
return r->drops == MAP_FAILED ? 0 : raindrop_size * r->capa;
|
68
|
+
}
|
69
|
+
|
70
|
+
static const rb_data_type_t rd_type = {
|
71
|
+
"raindrops",
|
72
|
+
{ rd_mark, rd_free, rd_memsize, /* reserved */ },
|
73
|
+
/* parent, data, [ flags ] */
|
74
|
+
};
|
75
|
+
|
54
76
|
/* automatically called at creation (before initialize) */
|
55
77
|
static VALUE alloc(VALUE klass)
|
56
78
|
{
|
57
79
|
struct raindrops *r;
|
58
|
-
VALUE rv =
|
80
|
+
VALUE rv = TypedData_Make_Struct(klass, struct raindrops, &rd_type, r);
|
59
81
|
|
60
82
|
r->drops = MAP_FAILED;
|
61
83
|
return rv;
|
@@ -65,7 +87,7 @@ static struct raindrops *get(VALUE self)
|
|
65
87
|
{
|
66
88
|
struct raindrops *r;
|
67
89
|
|
68
|
-
|
90
|
+
TypedData_Get_Struct(self, struct raindrops, &rd_type, r);
|
69
91
|
|
70
92
|
if (r->drops == MAP_FAILED)
|
71
93
|
rb_raise(rb_eStandardError, "invalid or freed Raindrops");
|
@@ -74,16 +96,10 @@ static struct raindrops *get(VALUE self)
|
|
74
96
|
}
|
75
97
|
|
76
98
|
/*
|
77
|
-
*
|
78
|
-
*
|
79
|
-
*
|
80
|
-
* Initializes a Raindrops object to hold +size+ counters. +size+ is
|
81
|
-
* only a hint and the actual number of counters the object has is
|
82
|
-
* dependent on the CPU model, number of cores, and page size of
|
83
|
-
* the machine. The actual size of the object will always be equal
|
84
|
-
* or greater than the specified +size+.
|
99
|
+
* This is the _actual_ implementation of #initialize - the Ruby wrapper
|
100
|
+
* handles keyword-argument handling then calls this method.
|
85
101
|
*/
|
86
|
-
static VALUE
|
102
|
+
static VALUE init_cimpl(VALUE self, VALUE size, VALUE io, VALUE zero)
|
87
103
|
{
|
88
104
|
struct raindrops *r = DATA_PTR(self);
|
89
105
|
int tries = 1;
|
@@ -100,11 +116,23 @@ static VALUE init(VALUE self, VALUE size)
|
|
100
116
|
r->capa = tmp / raindrop_size;
|
101
117
|
assert(PAGE_ALIGN(raindrop_size * r->capa) == tmp && "not aligned");
|
102
118
|
|
119
|
+
r->io = io;
|
120
|
+
|
103
121
|
retry:
|
104
|
-
r->
|
105
|
-
|
122
|
+
if (RTEST(r->io)) {
|
123
|
+
int fd = NUM2INT(rb_funcall(r->io, rb_intern("fileno"), 0));
|
124
|
+
rb_funcall(r->io, rb_intern("truncate"), 1, SIZET2NUM(tmp));
|
125
|
+
r->drops = mmap(NULL, tmp,
|
126
|
+
PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
|
127
|
+
} else {
|
128
|
+
r->drops = mmap(NULL, tmp,
|
129
|
+
PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED,
|
130
|
+
-1, 0);
|
131
|
+
}
|
106
132
|
if (r->drops == MAP_FAILED) {
|
107
|
-
|
133
|
+
int err = errno;
|
134
|
+
|
135
|
+
if ((err == EAGAIN || err == ENOMEM) && tries-- > 0) {
|
108
136
|
rb_gc();
|
109
137
|
goto retry;
|
110
138
|
}
|
@@ -112,6 +140,9 @@ retry:
|
|
112
140
|
}
|
113
141
|
r->pid = getpid();
|
114
142
|
|
143
|
+
if (RTEST(zero))
|
144
|
+
memset(r->drops, 0, tmp);
|
145
|
+
|
115
146
|
return self;
|
116
147
|
}
|
117
148
|
|
@@ -140,7 +171,9 @@ static void resize(struct raindrops *r, size_t new_rd_size)
|
|
140
171
|
|
141
172
|
rv = mremap(old_address, old_size, new_size, MREMAP_MAYMOVE);
|
142
173
|
if (rv == MAP_FAILED) {
|
143
|
-
|
174
|
+
int err = errno;
|
175
|
+
|
176
|
+
if (err == EAGAIN || err == ENOMEM) {
|
144
177
|
rb_gc();
|
145
178
|
rv = mremap(old_address, old_size, new_size, 0);
|
146
179
|
}
|
@@ -200,14 +233,16 @@ static VALUE capa(VALUE self)
|
|
200
233
|
* call-seq:
|
201
234
|
* rd.dup -> rd_copy
|
202
235
|
*
|
203
|
-
* Duplicates and snapshots the current state of a Raindrops object.
|
236
|
+
* Duplicates and snapshots the current state of a Raindrops object. Even
|
237
|
+
* if the given Raindrops object is backed by a file, the copy will be backed
|
238
|
+
* by independent, anonymously mapped memory.
|
204
239
|
*/
|
205
240
|
static VALUE init_copy(VALUE dest, VALUE source)
|
206
241
|
{
|
207
242
|
struct raindrops *dst = DATA_PTR(dest);
|
208
243
|
struct raindrops *src = get(source);
|
209
244
|
|
210
|
-
|
245
|
+
init_cimpl(dest, SIZET2NUM(src->size), Qnil, Qfalse);
|
211
246
|
memcpy(dst->drops, src->drops, raindrop_size * src->size);
|
212
247
|
|
213
248
|
return dest;
|
@@ -323,7 +358,9 @@ static VALUE aref(VALUE self, VALUE index)
|
|
323
358
|
|
324
359
|
#ifdef __linux__
|
325
360
|
void Init_raindrops_linux_inet_diag(void);
|
326
|
-
|
361
|
+
#endif
|
362
|
+
#ifdef HAVE_TYPE_STRUCT_TCP_INFO
|
363
|
+
void Init_raindrops_tcp_info(void);
|
327
364
|
#endif
|
328
365
|
|
329
366
|
#ifndef _SC_NPROCESSORS_CONF
|
@@ -356,6 +393,20 @@ static VALUE evaporate_bang(VALUE self)
|
|
356
393
|
return Qnil;
|
357
394
|
}
|
358
395
|
|
396
|
+
/*
|
397
|
+
* call-seq:
|
398
|
+
* to_io -> IO
|
399
|
+
*
|
400
|
+
* Returns the IO object backing the memory for this raindrop, if
|
401
|
+
* one was specified when constructing this Raindrop. If this
|
402
|
+
* Raindrop is backed by anonymous memory, this method returns nil.
|
403
|
+
*/
|
404
|
+
static VALUE to_io(VALUE self)
|
405
|
+
{
|
406
|
+
struct raindrops *r = get(self);
|
407
|
+
return r->io;
|
408
|
+
}
|
409
|
+
|
359
410
|
void Init_raindrops_ext(void)
|
360
411
|
{
|
361
412
|
VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
|
@@ -414,7 +465,7 @@ void Init_raindrops_ext(void)
|
|
414
465
|
|
415
466
|
rb_define_alloc_func(cRaindrops, alloc);
|
416
467
|
|
417
|
-
|
468
|
+
rb_define_private_method(cRaindrops, "initialize_cimpl", init_cimpl, 3);
|
418
469
|
rb_define_method(cRaindrops, "incr", incr, -1);
|
419
470
|
rb_define_method(cRaindrops, "decr", decr, -1);
|
420
471
|
rb_define_method(cRaindrops, "to_ary", to_ary, 0);
|
@@ -425,9 +476,12 @@ void Init_raindrops_ext(void)
|
|
425
476
|
rb_define_method(cRaindrops, "capa", capa, 0);
|
426
477
|
rb_define_method(cRaindrops, "initialize_copy", init_copy, 1);
|
427
478
|
rb_define_method(cRaindrops, "evaporate!", evaporate_bang, 0);
|
479
|
+
rb_define_method(cRaindrops, "to_io", to_io, 0);
|
428
480
|
|
429
481
|
#ifdef __linux__
|
430
482
|
Init_raindrops_linux_inet_diag();
|
431
|
-
|
483
|
+
#endif
|
484
|
+
#ifdef HAVE_TYPE_STRUCT_TCP_INFO
|
485
|
+
Init_raindrops_tcp_info();
|
432
486
|
#endif
|
433
487
|
}
|
@@ -0,0 +1,245 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <sys/socket.h>
|
3
|
+
#include <netinet/in.h>
|
4
|
+
#if defined(HAVE_LINUX_TCP_H)
|
5
|
+
# include <linux/tcp.h>
|
6
|
+
#else
|
7
|
+
# if defined(HAVE_NETINET_TCP_H)
|
8
|
+
# include <netinet/tcp.h>
|
9
|
+
# endif
|
10
|
+
# if defined(HAVE_NETINET_TCP_FSM_H)
|
11
|
+
# include <netinet/tcp_fsm.h>
|
12
|
+
# endif
|
13
|
+
#endif
|
14
|
+
|
15
|
+
#ifdef HAVE_TYPE_STRUCT_TCP_INFO
|
16
|
+
#include "my_fileno.h"
|
17
|
+
|
18
|
+
CFUNC_tcp_info_tcpi_state
|
19
|
+
CFUNC_tcp_info_tcpi_ca_state
|
20
|
+
CFUNC_tcp_info_tcpi_retransmits
|
21
|
+
CFUNC_tcp_info_tcpi_probes
|
22
|
+
CFUNC_tcp_info_tcpi_backoff
|
23
|
+
CFUNC_tcp_info_tcpi_options
|
24
|
+
CFUNC_tcp_info_tcpi_snd_wscale
|
25
|
+
CFUNC_tcp_info_tcpi_rcv_wscale
|
26
|
+
CFUNC_tcp_info_tcpi_rto
|
27
|
+
CFUNC_tcp_info_tcpi_ato
|
28
|
+
CFUNC_tcp_info_tcpi_snd_mss
|
29
|
+
CFUNC_tcp_info_tcpi_rcv_mss
|
30
|
+
CFUNC_tcp_info_tcpi_unacked
|
31
|
+
CFUNC_tcp_info_tcpi_sacked
|
32
|
+
CFUNC_tcp_info_tcpi_lost
|
33
|
+
CFUNC_tcp_info_tcpi_retrans
|
34
|
+
CFUNC_tcp_info_tcpi_fackets
|
35
|
+
CFUNC_tcp_info_tcpi_last_data_sent
|
36
|
+
CFUNC_tcp_info_tcpi_last_ack_sent
|
37
|
+
CFUNC_tcp_info_tcpi_last_data_recv
|
38
|
+
CFUNC_tcp_info_tcpi_last_ack_recv
|
39
|
+
CFUNC_tcp_info_tcpi_pmtu
|
40
|
+
CFUNC_tcp_info_tcpi_rcv_ssthresh
|
41
|
+
CFUNC_tcp_info_tcpi_rtt
|
42
|
+
CFUNC_tcp_info_tcpi_rttvar
|
43
|
+
CFUNC_tcp_info_tcpi_snd_ssthresh
|
44
|
+
CFUNC_tcp_info_tcpi_snd_cwnd
|
45
|
+
CFUNC_tcp_info_tcpi_advmss
|
46
|
+
CFUNC_tcp_info_tcpi_reordering
|
47
|
+
CFUNC_tcp_info_tcpi_rcv_rtt
|
48
|
+
CFUNC_tcp_info_tcpi_rcv_space
|
49
|
+
CFUNC_tcp_info_tcpi_total_retrans
|
50
|
+
|
51
|
+
static size_t tcpi_memsize(const void *ptr)
|
52
|
+
{
|
53
|
+
return sizeof(struct tcp_info);
|
54
|
+
}
|
55
|
+
|
56
|
+
static const rb_data_type_t tcpi_type = {
|
57
|
+
"tcp_info",
|
58
|
+
{ NULL, RUBY_TYPED_DEFAULT_FREE, tcpi_memsize, /* reserved */ },
|
59
|
+
/* parent, data, [ flags ] */
|
60
|
+
};
|
61
|
+
|
62
|
+
static VALUE alloc(VALUE klass)
|
63
|
+
{
|
64
|
+
struct tcp_info *info;
|
65
|
+
|
66
|
+
return TypedData_Make_Struct(klass, struct tcp_info, &tcpi_type, info);
|
67
|
+
}
|
68
|
+
|
69
|
+
/*
|
70
|
+
* call-seq:
|
71
|
+
*
|
72
|
+
* Raindrops::TCP_Info.new(tcp_socket) -> TCP_Info object
|
73
|
+
*
|
74
|
+
* Reads a TCP_Info object from any given +tcp_socket+. See the tcp(7)
|
75
|
+
* manpage and /usr/include/linux/tcp.h for more details.
|
76
|
+
*/
|
77
|
+
static VALUE init(VALUE self, VALUE io)
|
78
|
+
{
|
79
|
+
int fd = my_fileno(io);
|
80
|
+
struct tcp_info *info = DATA_PTR(self);
|
81
|
+
socklen_t len = (socklen_t)sizeof(struct tcp_info);
|
82
|
+
int rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, info, &len);
|
83
|
+
|
84
|
+
if (rc != 0)
|
85
|
+
rb_sys_fail("getsockopt");
|
86
|
+
|
87
|
+
return self;
|
88
|
+
}
|
89
|
+
|
90
|
+
void Init_raindrops_tcp_info(void)
|
91
|
+
{
|
92
|
+
VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
|
93
|
+
VALUE cTCP_Info;
|
94
|
+
|
95
|
+
/*
|
96
|
+
* Document-class: Raindrops::TCP_Info
|
97
|
+
*
|
98
|
+
* This is used to wrap "struct tcp_info" as described in tcp(7)
|
99
|
+
* and /usr/include/linux/tcp.h. The following readers methods
|
100
|
+
* are defined corresponding to the "tcpi_" fields in the
|
101
|
+
* tcp_info struct.
|
102
|
+
*
|
103
|
+
* As of raindrops 0.18.0+, this is supported on FreeBSD and OpenBSD
|
104
|
+
* systems as well as Linux, although not all fields exist or
|
105
|
+
* match the documentation, below.
|
106
|
+
*
|
107
|
+
* In particular, the +last_data_recv+ field is useful for measuring
|
108
|
+
* the amount of time a client spent in the listen queue before
|
109
|
+
* +accept()+, but only if +TCP_DEFER_ACCEPT+ is used with the
|
110
|
+
* listen socket (it is on by default in Unicorn).
|
111
|
+
*
|
112
|
+
* - state
|
113
|
+
* - ca_state
|
114
|
+
* - retransmits
|
115
|
+
* - probes
|
116
|
+
* - backoff
|
117
|
+
* - options
|
118
|
+
* - snd_wscale
|
119
|
+
* - rcv_wscale
|
120
|
+
* - rto
|
121
|
+
* - ato
|
122
|
+
* - snd_mss
|
123
|
+
* - rcv_mss
|
124
|
+
* - unacked
|
125
|
+
* - sacked
|
126
|
+
* - lost
|
127
|
+
* - retrans
|
128
|
+
* - fackets
|
129
|
+
* - last_data_sent
|
130
|
+
* - last_ack_sent
|
131
|
+
* - last_data_recv
|
132
|
+
* - last_ack_recv
|
133
|
+
* - pmtu
|
134
|
+
* - rcv_ssthresh
|
135
|
+
* - rtt
|
136
|
+
* - rttvar
|
137
|
+
* - snd_ssthresh
|
138
|
+
* - snd_cwnd
|
139
|
+
* - advmss
|
140
|
+
* - reordering
|
141
|
+
* - rcv_rtt
|
142
|
+
* - rcv_space
|
143
|
+
* - total_retrans
|
144
|
+
*
|
145
|
+
* https://kernel.org/doc/man-pages/online/pages/man7/tcp.7.html
|
146
|
+
*/
|
147
|
+
cTCP_Info = rb_define_class_under(cRaindrops, "TCP_Info", rb_cObject);
|
148
|
+
rb_define_alloc_func(cTCP_Info, alloc);
|
149
|
+
rb_define_private_method(cTCP_Info, "initialize", init, 1);
|
150
|
+
|
151
|
+
/*
|
152
|
+
* Document-method: Raindrops::TCP_Info#get!
|
153
|
+
*
|
154
|
+
* call-seq:
|
155
|
+
*
|
156
|
+
* info = Raindrops::TCP_Info.new(tcp_socket)
|
157
|
+
* info.get!(tcp_socket)
|
158
|
+
*
|
159
|
+
* Update an existing TCP_Info objects with the latest stats
|
160
|
+
* from the given socket. This even allows sharing TCP_Info
|
161
|
+
* objects between different sockets to avoid garbage.
|
162
|
+
*/
|
163
|
+
rb_define_method(cTCP_Info, "get!", init, 1);
|
164
|
+
|
165
|
+
DEFINE_METHOD_tcp_info_tcpi_state;
|
166
|
+
DEFINE_METHOD_tcp_info_tcpi_ca_state;
|
167
|
+
DEFINE_METHOD_tcp_info_tcpi_retransmits;
|
168
|
+
DEFINE_METHOD_tcp_info_tcpi_probes;
|
169
|
+
DEFINE_METHOD_tcp_info_tcpi_backoff;
|
170
|
+
DEFINE_METHOD_tcp_info_tcpi_options;
|
171
|
+
DEFINE_METHOD_tcp_info_tcpi_snd_wscale;
|
172
|
+
DEFINE_METHOD_tcp_info_tcpi_rcv_wscale;
|
173
|
+
DEFINE_METHOD_tcp_info_tcpi_rto;
|
174
|
+
DEFINE_METHOD_tcp_info_tcpi_ato;
|
175
|
+
DEFINE_METHOD_tcp_info_tcpi_snd_mss;
|
176
|
+
DEFINE_METHOD_tcp_info_tcpi_rcv_mss;
|
177
|
+
DEFINE_METHOD_tcp_info_tcpi_unacked;
|
178
|
+
DEFINE_METHOD_tcp_info_tcpi_sacked;
|
179
|
+
DEFINE_METHOD_tcp_info_tcpi_lost;
|
180
|
+
DEFINE_METHOD_tcp_info_tcpi_retrans;
|
181
|
+
DEFINE_METHOD_tcp_info_tcpi_fackets;
|
182
|
+
DEFINE_METHOD_tcp_info_tcpi_last_data_sent;
|
183
|
+
DEFINE_METHOD_tcp_info_tcpi_last_ack_sent;
|
184
|
+
DEFINE_METHOD_tcp_info_tcpi_last_data_recv;
|
185
|
+
DEFINE_METHOD_tcp_info_tcpi_last_ack_recv;
|
186
|
+
DEFINE_METHOD_tcp_info_tcpi_pmtu;
|
187
|
+
DEFINE_METHOD_tcp_info_tcpi_rcv_ssthresh;
|
188
|
+
DEFINE_METHOD_tcp_info_tcpi_rtt;
|
189
|
+
DEFINE_METHOD_tcp_info_tcpi_rttvar;
|
190
|
+
DEFINE_METHOD_tcp_info_tcpi_snd_ssthresh;
|
191
|
+
DEFINE_METHOD_tcp_info_tcpi_snd_cwnd;
|
192
|
+
DEFINE_METHOD_tcp_info_tcpi_advmss;
|
193
|
+
DEFINE_METHOD_tcp_info_tcpi_reordering;
|
194
|
+
DEFINE_METHOD_tcp_info_tcpi_rcv_rtt;
|
195
|
+
DEFINE_METHOD_tcp_info_tcpi_rcv_space;
|
196
|
+
DEFINE_METHOD_tcp_info_tcpi_total_retrans;
|
197
|
+
|
198
|
+
#ifdef RAINDROPS_TCP_STATES_ALL_KNOWN
|
199
|
+
|
200
|
+
/*
|
201
|
+
* Document-const: Raindrops::TCP
|
202
|
+
*
|
203
|
+
* This is a frozen hash storing the numeric values
|
204
|
+
* maps platform-independent symbol keys to
|
205
|
+
* platform-dependent numeric values. These states
|
206
|
+
* are all valid values for the Raindrops::TCP_Info#state field.
|
207
|
+
*
|
208
|
+
* The platform-independent names of the keys in this hash are:
|
209
|
+
*
|
210
|
+
* - :ESTABLISHED
|
211
|
+
* - :SYN_SENT
|
212
|
+
* - :SYN_RECV
|
213
|
+
* - :FIN_WAIT1
|
214
|
+
* - :FIN_WAIT2
|
215
|
+
* - :TIME_WAIT
|
216
|
+
* - :CLOSE
|
217
|
+
* - :CLOSE_WAIT
|
218
|
+
* - :LAST_ACK
|
219
|
+
* - :LISTEN
|
220
|
+
* - :CLOSING
|
221
|
+
*
|
222
|
+
* This is only supported on platforms where TCP_Info is supported,
|
223
|
+
* currently FreeBSD, OpenBSD, and Linux-based systems.
|
224
|
+
*/
|
225
|
+
{
|
226
|
+
#define TCPSET(n,v) rb_hash_aset(tcp, ID2SYM(rb_intern(#n)), INT2NUM(v))
|
227
|
+
VALUE tcp = rb_hash_new();
|
228
|
+
TCPSET(ESTABLISHED, RAINDROPS_TCP_ESTABLISHED);
|
229
|
+
TCPSET(SYN_SENT, RAINDROPS_TCP_SYN_SENT);
|
230
|
+
TCPSET(SYN_RECV, RAINDROPS_TCP_SYN_RECV);
|
231
|
+
TCPSET(FIN_WAIT1, RAINDROPS_TCP_FIN_WAIT1);
|
232
|
+
TCPSET(FIN_WAIT2, RAINDROPS_TCP_FIN_WAIT2);
|
233
|
+
TCPSET(TIME_WAIT, RAINDROPS_TCP_TIME_WAIT);
|
234
|
+
TCPSET(CLOSE, RAINDROPS_TCP_CLOSE);
|
235
|
+
TCPSET(CLOSE_WAIT, RAINDROPS_TCP_CLOSE_WAIT);
|
236
|
+
TCPSET(LAST_ACK, RAINDROPS_TCP_LAST_ACK);
|
237
|
+
TCPSET(LISTEN, RAINDROPS_TCP_LISTEN);
|
238
|
+
TCPSET(CLOSING, RAINDROPS_TCP_CLOSING);
|
239
|
+
#undef TCPSET
|
240
|
+
OBJ_FREEZE(tcp);
|
241
|
+
rb_define_const(cRaindrops, "TCP", tcp);
|
242
|
+
}
|
243
|
+
#endif
|
244
|
+
}
|
245
|
+
#endif /* HAVE_STRUCT_TCP_INFO */
|