raindrops 0.16.0 → 0.20.0
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 +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 */
|