From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.0 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 8293920953 for ; Thu, 16 Mar 2017 03:16:54 +0000 (UTC) From: Eric Wong To: raindrops-public@bogomips.org Subject: [PATCH 1/2] tcp_info: support this struct under FreeBSD Date: Thu, 16 Mar 2017 03:16:51 +0000 Message-Id: <20170316031652.17433-2-e@80x24.org> In-Reply-To: <20170316031652.17433-1-e@80x24.org> References: <20170316031652.17433-1-e@80x24.org> List-Id: Of course these fields are not portable between Linux and FreeBSD, but they should remain ABI-compatible for future versions of each OS. Tested on FreeBSD 10.3-RELEASE i386 TCP state names will be another problem... --- ext/raindrops/extconf.rb | 78 ++++++++++- ext/raindrops/raindrops.c | 8 +- ext/raindrops/{linux_tcp_info.c => tcp_info.c} | 159 +++++++++++----------- test/{test_linux_tcp_info.rb => test_tcp_info.rb} | 37 +++-- 4 files changed, 189 insertions(+), 93 deletions(-) rename ext/raindrops/{linux_tcp_info.c => tcp_info.c} (51%) rename test/{test_linux_tcp_info.rb => test_tcp_info.rb} (66%) diff --git a/ext/raindrops/extconf.rb b/ext/raindrops/extconf.rb index 79d212c..5273b74 100644 --- a/ext/raindrops/extconf.rb +++ b/ext/raindrops/extconf.rb @@ -1,4 +1,5 @@ require 'mkmf' +require 'shellwords' dir_config('atomic_ops') have_func('mmap', 'sys/mman.h') or abort 'mmap() not found' @@ -6,9 +7,83 @@ $CPPFLAGS += " -D_GNU_SOURCE " have_func('mremap', 'sys/mman.h') -have_header('linux/tcp.h') +headers = %w(sys/types.h netdb.h string.h sys/socket.h netinet/in.h) +if have_header('linux/tcp.h') + headers << 'linux/tcp.h' +else + %w(netinet/tcp.h netinet/tcp_fsm.h).each { |h| + have_header(h, headers) and headers << h + } +end $CPPFLAGS += " -D_BSD_SOURCE " + +if have_type("struct tcp_info", headers) + %w( + tcpi_state + tcpi_ca_state + tcpi_retransmits + tcpi_probes + tcpi_backoff + tcpi_options + tcpi_snd_wscale + tcpi_rcv_wscale + tcpi_rto + tcpi_ato + tcpi_snd_mss + tcpi_rcv_mss + tcpi_unacked + tcpi_sacked + tcpi_lost + tcpi_retrans + tcpi_fackets + tcpi_last_data_sent + tcpi_last_ack_sent + tcpi_last_data_recv + tcpi_last_ack_recv + tcpi_pmtu + tcpi_rcv_ssthresh + tcpi_rtt + tcpi_rttvar + tcpi_snd_ssthresh + tcpi_snd_cwnd + tcpi_advmss + tcpi_reordering + tcpi_rcv_rtt + tcpi_rcv_space + tcpi_total_retrans + tcpi_snd_wnd + tcpi_snd_bwnd + tcpi_snd_nxt + tcpi_rcv_nxt + tcpi_toe_tid + tcpi_snd_rexmitpack + tcpi_rcv_ooopack + tcpi_snd_zerowin + ).each do |field| + cfunc = "tcp_info_#{field}" + if have_struct_member('struct tcp_info', field, headers) + func_body = <#{field}); +} +EOF + func_body.delete!("\n") + $defs << "-DCFUNC_#{cfunc}=#{Shellwords.shellescape(func_body)}" + else + func_body = "static inline void #{cfunc}(void) {}" + $defs << "-DCFUNC_#{cfunc}=#{Shellwords.shellescape(func_body)}" + cfunc = 'rb_f_notimplement'.freeze + end + rbmethod = %Q("#{field.sub(/\Atcpi_/, ''.freeze)}") + $defs << "-DDEFINE_METHOD_tcp_info_#{field}=" \ + "#{Shellwords.shellescape( + %Q[rb_define_method(cTCP_Info,#{rbmethod},#{cfunc},0)])}" + end +end + have_func("getpagesize", "unistd.h") have_func('rb_thread_blocking_region') have_func('rb_thread_io_blocking_region') @@ -53,4 +128,5 @@ apt-get install libatomic-ops-dev SRC +create_header # generate extconf.h to avoid excessively long command-line create_makefile('raindrops_ext') diff --git a/ext/raindrops/raindrops.c b/ext/raindrops/raindrops.c index 390b8b8..0ea3e32 100644 --- a/ext/raindrops/raindrops.c +++ b/ext/raindrops/raindrops.c @@ -336,7 +336,9 @@ static VALUE aref(VALUE self, VALUE index) #ifdef __linux__ void Init_raindrops_linux_inet_diag(void); -void Init_raindrops_linux_tcp_info(void); +#endif +#ifdef HAVE_TYPE_STRUCT_TCP_INFO +void Init_raindrops_tcp_info(void); #endif #ifndef _SC_NPROCESSORS_CONF @@ -441,6 +443,8 @@ void Init_raindrops_ext(void) #ifdef __linux__ Init_raindrops_linux_inet_diag(); - Init_raindrops_linux_tcp_info(); +#endif +#ifdef HAVE_TYPE_STRUCT_TCP_INFO + Init_raindrops_tcp_info(); #endif } diff --git a/ext/raindrops/linux_tcp_info.c b/ext/raindrops/tcp_info.c similarity index 51% rename from ext/raindrops/linux_tcp_info.c rename to ext/raindrops/tcp_info.c index 83001a5..dc615f7 100644 --- a/ext/raindrops/linux_tcp_info.c +++ b/ext/raindrops/tcp_info.c @@ -1,50 +1,53 @@ -#if defined(__linux__) && defined(HAVE_LINUX_TCP_H) #include +#include "extconf.h" #include #include -#include -#ifdef TCP_INFO -#include "my_fileno.h" +#if defined(HAVE_LINUX_TCP_H) +# include +#else +# if defined(HAVE_NETINET_TCP_H) +# include +# endif +# if defined(HAVE_NETINET_TCP_FSM_H) +# include +# endif +#endif -#define TCPI_ATTR_READER(x) \ -static VALUE tcp_info_##x(VALUE self) \ -{ \ - struct tcp_info *info = DATA_PTR(self); \ - return UINT2NUM((uint32_t)info->tcpi_##x); \ -} +#ifdef HAVE_TYPE_STRUCT_TCP_INFO +#include "my_fileno.h" -TCPI_ATTR_READER(state) -TCPI_ATTR_READER(ca_state) -TCPI_ATTR_READER(retransmits) -TCPI_ATTR_READER(probes) -TCPI_ATTR_READER(backoff) -TCPI_ATTR_READER(options) -TCPI_ATTR_READER(snd_wscale) -TCPI_ATTR_READER(rcv_wscale) -TCPI_ATTR_READER(rto) -TCPI_ATTR_READER(ato) -TCPI_ATTR_READER(snd_mss) -TCPI_ATTR_READER(rcv_mss) -TCPI_ATTR_READER(unacked) -TCPI_ATTR_READER(sacked) -TCPI_ATTR_READER(lost) -TCPI_ATTR_READER(retrans) -TCPI_ATTR_READER(fackets) -TCPI_ATTR_READER(last_data_sent) -TCPI_ATTR_READER(last_ack_sent) -TCPI_ATTR_READER(last_data_recv) -TCPI_ATTR_READER(last_ack_recv) -TCPI_ATTR_READER(pmtu) -TCPI_ATTR_READER(rcv_ssthresh) -TCPI_ATTR_READER(rtt) -TCPI_ATTR_READER(rttvar) -TCPI_ATTR_READER(snd_ssthresh) -TCPI_ATTR_READER(snd_cwnd) -TCPI_ATTR_READER(advmss) -TCPI_ATTR_READER(reordering) -TCPI_ATTR_READER(rcv_rtt) -TCPI_ATTR_READER(rcv_space) -TCPI_ATTR_READER(total_retrans) +CFUNC_tcp_info_tcpi_state +CFUNC_tcp_info_tcpi_ca_state +CFUNC_tcp_info_tcpi_retransmits +CFUNC_tcp_info_tcpi_probes +CFUNC_tcp_info_tcpi_backoff +CFUNC_tcp_info_tcpi_options +CFUNC_tcp_info_tcpi_snd_wscale +CFUNC_tcp_info_tcpi_rcv_wscale +CFUNC_tcp_info_tcpi_rto +CFUNC_tcp_info_tcpi_ato +CFUNC_tcp_info_tcpi_snd_mss +CFUNC_tcp_info_tcpi_rcv_mss +CFUNC_tcp_info_tcpi_unacked +CFUNC_tcp_info_tcpi_sacked +CFUNC_tcp_info_tcpi_lost +CFUNC_tcp_info_tcpi_retrans +CFUNC_tcp_info_tcpi_fackets +CFUNC_tcp_info_tcpi_last_data_sent +CFUNC_tcp_info_tcpi_last_ack_sent +CFUNC_tcp_info_tcpi_last_data_recv +CFUNC_tcp_info_tcpi_last_ack_recv +CFUNC_tcp_info_tcpi_pmtu +CFUNC_tcp_info_tcpi_rcv_ssthresh +CFUNC_tcp_info_tcpi_rtt +CFUNC_tcp_info_tcpi_rttvar +CFUNC_tcp_info_tcpi_snd_ssthresh +CFUNC_tcp_info_tcpi_snd_cwnd +CFUNC_tcp_info_tcpi_advmss +CFUNC_tcp_info_tcpi_reordering +CFUNC_tcp_info_tcpi_rcv_rtt +CFUNC_tcp_info_tcpi_rcv_space +CFUNC_tcp_info_tcpi_total_retrans static size_t tcpi_memsize(const void *ptr) { @@ -85,7 +88,7 @@ static VALUE init(VALUE self, VALUE io) return self; } -void Init_raindrops_linux_tcp_info(void) +void Init_raindrops_tcp_info(void) { VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject); VALUE cTCP_Info; @@ -156,41 +159,37 @@ void Init_raindrops_linux_tcp_info(void) */ rb_define_method(cTCP_Info, "get!", init, 1); -#define TCPI_DEFINE_METHOD(x) \ - rb_define_method(cTCP_Info, #x, tcp_info_##x, 0) - - TCPI_DEFINE_METHOD(state); - TCPI_DEFINE_METHOD(ca_state); - TCPI_DEFINE_METHOD(retransmits); - TCPI_DEFINE_METHOD(probes); - TCPI_DEFINE_METHOD(backoff); - TCPI_DEFINE_METHOD(options); - TCPI_DEFINE_METHOD(snd_wscale); - TCPI_DEFINE_METHOD(rcv_wscale); - TCPI_DEFINE_METHOD(rto); - TCPI_DEFINE_METHOD(ato); - TCPI_DEFINE_METHOD(snd_mss); - TCPI_DEFINE_METHOD(rcv_mss); - TCPI_DEFINE_METHOD(unacked); - TCPI_DEFINE_METHOD(sacked); - TCPI_DEFINE_METHOD(lost); - TCPI_DEFINE_METHOD(retrans); - TCPI_DEFINE_METHOD(fackets); - TCPI_DEFINE_METHOD(last_data_sent); - TCPI_DEFINE_METHOD(last_ack_sent); - TCPI_DEFINE_METHOD(last_data_recv); - TCPI_DEFINE_METHOD(last_ack_recv); - TCPI_DEFINE_METHOD(pmtu); - TCPI_DEFINE_METHOD(rcv_ssthresh); - TCPI_DEFINE_METHOD(rtt); - TCPI_DEFINE_METHOD(rttvar); - TCPI_DEFINE_METHOD(snd_ssthresh); - TCPI_DEFINE_METHOD(snd_cwnd); - TCPI_DEFINE_METHOD(advmss); - TCPI_DEFINE_METHOD(reordering); - TCPI_DEFINE_METHOD(rcv_rtt); - TCPI_DEFINE_METHOD(rcv_space); - TCPI_DEFINE_METHOD(total_retrans); + DEFINE_METHOD_tcp_info_tcpi_state; + DEFINE_METHOD_tcp_info_tcpi_ca_state; + DEFINE_METHOD_tcp_info_tcpi_retransmits; + DEFINE_METHOD_tcp_info_tcpi_probes; + DEFINE_METHOD_tcp_info_tcpi_backoff; + DEFINE_METHOD_tcp_info_tcpi_options; + DEFINE_METHOD_tcp_info_tcpi_snd_wscale; + DEFINE_METHOD_tcp_info_tcpi_rcv_wscale; + DEFINE_METHOD_tcp_info_tcpi_rto; + DEFINE_METHOD_tcp_info_tcpi_ato; + DEFINE_METHOD_tcp_info_tcpi_snd_mss; + DEFINE_METHOD_tcp_info_tcpi_rcv_mss; + DEFINE_METHOD_tcp_info_tcpi_unacked; + DEFINE_METHOD_tcp_info_tcpi_sacked; + DEFINE_METHOD_tcp_info_tcpi_lost; + DEFINE_METHOD_tcp_info_tcpi_retrans; + DEFINE_METHOD_tcp_info_tcpi_fackets; + DEFINE_METHOD_tcp_info_tcpi_last_data_sent; + DEFINE_METHOD_tcp_info_tcpi_last_ack_sent; + DEFINE_METHOD_tcp_info_tcpi_last_data_recv; + DEFINE_METHOD_tcp_info_tcpi_last_ack_recv; + DEFINE_METHOD_tcp_info_tcpi_pmtu; + DEFINE_METHOD_tcp_info_tcpi_rcv_ssthresh; + DEFINE_METHOD_tcp_info_tcpi_rtt; + DEFINE_METHOD_tcp_info_tcpi_rttvar; + DEFINE_METHOD_tcp_info_tcpi_snd_ssthresh; + DEFINE_METHOD_tcp_info_tcpi_snd_cwnd; + DEFINE_METHOD_tcp_info_tcpi_advmss; + DEFINE_METHOD_tcp_info_tcpi_reordering; + DEFINE_METHOD_tcp_info_tcpi_rcv_rtt; + DEFINE_METHOD_tcp_info_tcpi_rcv_space; + DEFINE_METHOD_tcp_info_tcpi_total_retrans; } -#endif /* TCP_INFO */ -#endif /* defined(__linux__) && defined(HAVE_LINUX_TCP_H) */ +#endif /* HAVE_STRUCT_TCP_INFO */ diff --git a/test/test_linux_tcp_info.rb b/test/test_tcp_info.rb similarity index 66% rename from test/test_linux_tcp_info.rb rename to test/test_tcp_info.rb index c947211..15df087 100644 --- a/test/test_linux_tcp_info.rb +++ b/test/test_tcp_info.rb @@ -5,15 +5,15 @@ require 'socket' require 'pp' $stderr.sync = $stdout.sync = true -class TestLinuxTCP_Info < Test::Unit::TestCase +class TestTCP_Info < Test::Unit::TestCase TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1' # Linux kernel commit 5ee3afba88f5a79d0bff07ddd87af45919259f91 TCP_INFO_useful_listenq = `uname -r`.strip >= '2.6.24' - - def test_tcp_server + def test_tcp_server_unacked + return if RUBY_PLATFORM !~ /linux/ # unacked not implemented on others... s = TCPServer.new(TEST_ADDR, 0) rv = Raindrops::TCP_Info.new s c = TCPSocket.new TEST_ADDR, s.addr[1] @@ -29,10 +29,8 @@ def test_tcp_server tmp.get!(s) assert_equal before, tmp.object_id - ensure - c.close if c - a.close if a - s.close + ensure + [ c, a, s ].compact.each(&:close) end def test_accessors @@ -42,12 +40,14 @@ def test_accessors assert tcp_info_methods.size >= 32 tcp_info_methods.each do |m| next if m.to_sym == :get! + next if ! tmp.respond_to?(m) val = tmp.__send__ m assert_kind_of Integer, val assert val >= 0 end - ensure - s.close + assert tmp.respond_to?(:state), 'every OS knows about TCP state, right?' + ensure + s.close end def test_tcp_server_delayed @@ -65,4 +65,21 @@ def test_tcp_server_delayed a.close if a s.close end -end if RUBY_PLATFORM =~ /linux/ + + def test_tcp_server_state_closed + s = TCPServer.new(TEST_ADDR, 0) + c = TCPSocket.new(TEST_ADDR, s.addr[1]) + i = Raindrops::TCP_Info.allocate + a = s.accept + i.get!(a) + state = i.state + c = c.close + sleep(0.01) # wait for kernel to update state + i.get!(a) + assert_not_equal state, i.state + ensure + s.close if s + c.close if c + a.close if a + end +end if defined? Raindrops::TCP_Info -- EW