Raindrops::Linux

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

Constants

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

Public Class Methods

tcp_listener_stats([addrs[, sock]]) => hash click to toggle source

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.

static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
{
        VALUE *ary;
        long i;
        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:
                ary = RARRAY_PTR(addrs);
                i = RARRAY_LEN(addrs);
                if (i == 1) {
                        rb_hash_aset(rv, *ary, tcp_stats(&args, *ary));
                        return rv;
                }
                for (; --i >= 0; ary++) {
                        union any_addr check;

                        parse_addr(&check, *ary);
                        rb_hash_aset(rv, *ary, Qtrue);
                }
                /* 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(rb_thread_io_blocking_region(diag, &args, args.fd));

        st_foreach(args.table, NIL_P(addrs) ? st_to_hash : st_AND_hash, rv);
        st_free_table(args.table);

        /* let GC deal with corner cases */
        if (argc < 2) rb_io_close(sock);
        return rv;
}
unix_listener_stats(paths = nil) click to toggle source

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.

    # File lib/raindrops/linux.rb, line 37
37:   def unix_listener_stats(paths = nil)
38:     rv = Hash.new { |h,k| h[k.freeze] = Raindrops::ListenStats.new(0, 0) }
39:     if nil == paths
40:       paths = [ '[^\n]+' ]
41:     else
42:       paths = paths.map do |path|
43:         path = path.dup
44:         path.force_encoding(Encoding::BINARY) if defined?(Encoding)
45:         rv[path]
46:         Regexp.escape(path)
47:       end
48:     end
49:     paths = /^\w+: \d+ \d+ 00000000 \d+ (\d+)\s+\d+ (#{paths.join('|')})$/
50: 
51:     # no point in pread since we can't stat for size on this file
52:     File.read(*PROC_NET_UNIX_ARGS).scan(paths) do |s|
53:       path = s[1]
54:       case s[0].to_i
55:       when 2 then rv[path].queued += 1
56:       when 3 then rv[path].active += 1
57:       end
58:     end
59: 
60:     rv
61:   end

Private Instance Methods

unix_listener_stats(paths = nil) click to toggle source

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.

    # File lib/raindrops/linux.rb, line 37
37:   def unix_listener_stats(paths = nil)
38:     rv = Hash.new { |h,k| h[k.freeze] = Raindrops::ListenStats.new(0, 0) }
39:     if nil == paths
40:       paths = [ '[^\n]+' ]
41:     else
42:       paths = paths.map do |path|
43:         path = path.dup
44:         path.force_encoding(Encoding::BINARY) if defined?(Encoding)
45:         rv[path]
46:         Regexp.escape(path)
47:       end
48:     end
49:     paths = /^\w+: \d+ \d+ 00000000 \d+ (\d+)\s+\d+ (#{paths.join('|')})$/
50: 
51:     # no point in pread since we can't stat for size on this file
52:     File.read(*PROC_NET_UNIX_ARGS).scan(paths) do |s|
53:       path = s[1]
54:       case s[0].to_i
55:       when 2 then rv[path].queued += 1
56:       when 3 then rv[path].active += 1
57:       end
58:     end
59: 
60:     rv
61:   end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.