Module: Raindrops::Linux
- Included in:
- Watcher
- Defined in:
- lib/raindrops/linux.rb,
ext/raindrops/linux_inet_diag.c
Overview
For reporting TCP ListenStats, users of older Linux kernels need to ensure that the the “inet_diag” and “tcp_diag” kernel modules are loaded as they do not autoload correctly. The inet_diag facilities of Raindrops is useful for periodic snapshot reporting of listen queue sizes.
Instead of snapshotting, Raindrops::Aggregate::LastDataRecv may be used to aggregate statistics from all
accepted sockets as they arrive based on the last_data_recv
field in Raindrops::TCP_Info
Constant Summary collapse
- PROC_NET_UNIX_ARGS =
The standard proc path for active UNIX domain sockets, feel free to call String#replace on this if your /proc is mounted in a non-standard location for whatever reason
[ '/proc/net/unix', { encoding: "binary" }]
Class Method Summary collapse
-
.Raindrops::Linux.tcp_listener_stats([addrs[, sock]]) ⇒ Hash
If specified,
addr
may be a string or array of strings representing listen addresses to filter for. -
.unix_listener_stats(paths = nil) ⇒ Object
Get ListenStats from an array of
paths
.
Class Method Details
.Raindrops::Linux.tcp_listener_stats([addrs[, sock]]) ⇒ Hash
If specified, addr
may be a string or array of strings representing listen addresses to filter for. Returns a hash with given addresses as keys and ListenStats objects as the values or a hash of all addresses.
addrs = %w(0.0.0.0:80 127.0.0.1:8080)
If addr
is nil or not specified, all (IPv4) addresses are returned. If sock
is specified, it should be a Raindrops::InetDiagSock object.
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 |
# File 'ext/raindrops/linux_inet_diag.c', line 614
static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
{
VALUE rv = rb_hash_new();
struct nogvl_args args;
VALUE addrs, sock;
rb_scan_args(argc, argv, "02", &addrs, &sock);
/*
* allocating page_size instead of OP_LEN since we'll reuse the
* buffer for recvmsg() later, we already checked for
* OPLEN <= page_size at initialization
*/
args.iov[2].iov_len = OPLEN;
args.iov[2].iov_base = alloca(page_size);
args.table = NULL;
if (NIL_P(sock))
sock = rb_funcall(cIDSock, id_new, 0);
args.fd = my_fileno(sock);
switch (TYPE(addrs)) {
case T_STRING:
rb_hash_aset(rv, addrs, tcp_stats(&args, addrs));
return rv;
case T_ARRAY: {
long i;
long len = RARRAY_LEN(addrs);
if (len == 1) {
VALUE cur = rb_ary_entry(addrs, 0);
rb_hash_aset(rv, cur, tcp_stats(&args, cur));
return rv;
}
for (i = 0; i < len; i++) {
union any_addr check;
VALUE cur = rb_ary_entry(addrs, i);
parse_addr(&check, cur);
rb_hash_aset(rv, cur, Qtrue /* placeholder */);
}
/* fall through */
}
case T_NIL:
args.table = st_init_strtable();
gen_bytecode_all(&args.iov[2]);
break;
default:
rb_raise(rb_eArgError,
"addr must be an array of strings, a string, or nil");
}
nl_errcheck(rd_fd_region(diag, &args, args.fd));
st_foreach(args.table, NIL_P(addrs) ? st_to_hash : st_AND_hash, rv);
st_free_table(args.table);
if (RHASH_SIZE(rv) > 1)
rb_hash_foreach(rv, drop_placeholders, Qfalse);
/* let GC deal with corner cases */
if (argc < 2) rb_io_close(sock);
return rv;
}
|
.unix_listener_stats(paths = nil) ⇒ Object
Get ListenStats from an array of paths
Socket state mapping from integer => symbol, based on socket_state enum from include/linux/net.h in the Linux kernel:
typedef enum {
SS_FREE = 0, /* not allocated */
SS_UNCONNECTED, /* unconnected to any socket */
SS_CONNECTING, /* in process of connecting */
SS_CONNECTED, /* connected to socket */
SS_DISCONNECTING /* in process of disconnecting */
} socket_state;
-
SS_CONNECTING maps to ListenStats#queued
-
SS_CONNECTED maps to ListenStats#active
This method may be significantly slower than its tcp_listener_stats counterpart due to the latter being able to use inet_diag via netlink. This parses /proc/net/unix as there is no other (known) way to expose Unix domain socket statistics over netlink.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/raindrops/linux.rb', line 37 def unix_listener_stats(paths = nil) rv = Hash.new { |h,k| h[k.freeze] = Raindrops::ListenStats.new(0, 0) } if nil == paths paths = [ '[^\n]+' ] else paths = paths.map do |path| path = path.dup path.force_encoding(Encoding::BINARY) if File.symlink?(path) link = path path = File.readlink(link) path.force_encoding(Encoding::BINARY) rv[link] = rv[path] # vivify ListenerStats else rv[path] # vivify ListenerStats end Regexp.escape(path) end end paths = /^\w+: \d+ \d+ (\d+) \d+ (\d+)\s+\d+ (#{paths.join('|')})$/n # no point in pread since we can't stat for size on this file File.read(PROC_NET_UNIX_ARGS[0], encoding: 'binary').scan(paths) do |s| path = s[-1] case s[0] when "00000000" # client sockets case s[1].to_i when 2 then rv[path].queued += 1 when 3 then rv[path].active += 1 end else # listeners, vivify empty stats rv[path] end end rv end |