diff options
| -rw-r--r-- | extendedstats/other.go | 22 | ||||
| -rw-r--r-- | extendedstats/unix.go | 72 | ||||
| -rw-r--r-- | networkQuality.go | 54 | ||||
| -rw-r--r-- | utilities/tcpinfo_other.go | 18 | ||||
| -rw-r--r-- | utilities/tcpinfo_unix.go | 62 | ||||
| -rw-r--r-- | utilities/utilities.go | 11 |
6 files changed, 127 insertions, 112 deletions
diff --git a/extendedstats/other.go b/extendedstats/other.go new file mode 100644 index 0000000..c0f89ed --- /dev/null +++ b/extendedstats/other.go @@ -0,0 +1,22 @@ +//go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd + +package extendedstats + +import ( + "net" + + "golang.org/x/sys/unix" +) + +type ExtendedStats struct{} + +func (es *ExtendedStats) IncorporateConnectionStats(conn net.Conn) {} + +func (es *ExtendedStats) Repr() string { + return "" +} + +func ExtendedStatsAvailable() bool { + return false +} diff --git a/extendedstats/unix.go b/extendedstats/unix.go new file mode 100644 index 0000000..e50d719 --- /dev/null +++ b/extendedstats/unix.go @@ -0,0 +1,72 @@ +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd + +package extendedstats + +import ( + "crypto/tls" + "fmt" + "net" + + "github.com/network-quality/goresponsiveness/utilities" + "golang.org/x/sys/unix" +) + +type ExtendedStats struct { + MaxPathMtu uint64 + TotalRetransmissions uint64 + AverageRtt float64 + rtt_measurements uint64 + total_rtt float64 +} + +func ExtendedStatsAvailable() bool { + return true +} + +func (es *ExtendedStats) IncorporateConnectionStats(rawConn net.Conn) { + tlsConn, ok := rawConn.(*tls.Conn) + if !ok { + fmt.Printf("OOPS: Could not get the TCP info for the connection (not a TLS connection)!\n") + } + tcpConn, ok := tlsConn.NetConn().(*net.TCPConn) + if !ok { + fmt.Printf("OOPS: Could not get the TCP info for the connection (not a TCP connection)!\n") + } + if info, err := getTCPInfo(tcpConn); err != nil { + fmt.Printf("OOPS: Could not get the TCP info for the connection: %v!\n", err) + } else { + es.MaxPathMtu = utilities.Max(es.MaxPathMtu, uint64(info.Pmtu)) + // https://lkml.iu.edu/hypermail/linux/kernel/1705.0/01790.html + es.TotalRetransmissions += uint64(info.Total_retrans) + es.total_rtt += float64(info.Rtt) + es.rtt_measurements += 1 + es.AverageRtt = es.total_rtt / float64(es.rtt_measurements) + + } +} + +func (es *ExtendedStats) Repr() string { + return fmt.Sprintf(`Extended Statistics: + Maximum Path MTU: %v + Total Retransmissions: %v + Average RTT: %v +`, es.MaxPathMtu, es.TotalRetransmissions, es.AverageRtt) +} + +func getTCPInfo(connection net.Conn) (*unix.TCPInfo, error) { + tcpConn, ok := connection.(*net.TCPConn) + if !ok { + return nil, fmt.Errorf("connection is not a net.TCPConn") + } + rawConn, err := tcpConn.SyscallConn() + if err != nil { + return nil, err + } + + var info *unix.TCPInfo = nil + rawConn.Control(func(fd uintptr) { + info, err = unix.GetsockoptTCPInfo(int(fd), unix.SOL_TCP, unix.TCP_INFO) + }) + return info, err +} diff --git a/networkQuality.go b/networkQuality.go index d9bdc94..217ee6a 100644 --- a/networkQuality.go +++ b/networkQuality.go @@ -19,9 +19,6 @@ import ( "crypto/tls" "flag" "fmt" - _ "io" - _ "log" - "net" "net/http" "os" "runtime/pprof" @@ -31,6 +28,7 @@ import ( "github.com/network-quality/goresponsiveness/config" "github.com/network-quality/goresponsiveness/constants" "github.com/network-quality/goresponsiveness/debug" + "github.com/network-quality/goresponsiveness/extendedstats" "github.com/network-quality/goresponsiveness/lgc" "github.com/network-quality/goresponsiveness/rpm" "github.com/network-quality/goresponsiveness/timeoutat" @@ -80,6 +78,12 @@ var ( "", "Enable client runtime profiling and specify storage location. Disabled by default.", ) + + calculateExtendedStats = flag.Bool( + "extended-stats", + false, + "Enable the collection and display of extended statistics -- may not be available on certain platforms.", + ) ) func main() { @@ -99,6 +103,11 @@ func main() { debugLevel = debug.Debug } + if *calculateExtendedStats && !extendedstats.ExtendedStatsAvailable() { + *calculateExtendedStats = false + fmt.Printf("Warning: Calculation of extended statics was requested but they are not supported on this platform.\n") + } + if err := config.Get(configHostPort, *configPath); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) return @@ -295,6 +304,18 @@ func main() { totalMeasurements := uint64(0) totalMeasurementTimes := float64(0) measurementTimeout := false + extendedStats := extendedstats.ExtendedStats{} + + for i := 0; i < len(downloadSaturation.LGCs); i++ { + // Assume that extended statistics are available -- the check was done explicitly at + // program startup if the calculateExtendedStats flag was set by the user on the command line. + if *calculateExtendedStats { + if !extendedstats.ExtendedStatsAvailable() { + panic("Extended stats are not available but the user requested their calculation.") + } + extendedStats.IncorporateConnectionStats(downloadSaturation.LGCs[i].Stats().ConnInfo.Conn) + } + } for i := 0; i < constants.MeasurementProbeCount && !measurementTimeout; i++ { if len(downloadSaturation.LGCs) == 0 { @@ -324,29 +345,6 @@ func main() { continue } - if *debugCliFlag { - // Note: This code is just an example of how to use utilities.GetTCPInfo. - rawConn := downloadSaturation.LGCs[randomLGCsIndex].Stats().ConnInfo.Conn - tlsConn, ok := rawConn.(*tls.Conn) - if !ok { - fmt.Printf("OOPS: Could not get the TCP info for the connection (not a TLS connection)!\n") - } - tcpConn, ok := tlsConn.NetConn().(*net.TCPConn) - if !ok { - fmt.Printf("OOPS: Could not get the TCP info for the connection (not a TCP connection)!\n") - } - if info, err := utilities.GetTCPInfo(tcpConn); err != nil { - switch err.(type) { - case *utilities.NotImplemented: - fmt.Printf("GetTCPInfo not implemented on this platform.\n") - default: - fmt.Printf("OOPS: Could not get the TCP info for the connection: %v!\n", err) - } - } else { - utilities.PrintTCPInfo(info) - } - } - unsaturatedMeasurementTransport := http2.Transport{} unsaturatedMeasurementTransport.TLSClientConfig = &tls.Config{} if sslKeyFileConcurrentWriter != nil { @@ -428,6 +426,10 @@ func main() { fmt.Printf("Error occurred calculating RPM -- no probe measurements received.\n") } + if *calculateExtendedStats { + fmt.Printf(extendedStats.Repr()) + } + cancelOperatingCtx() if *debugCliFlag { fmt.Printf("In debugging mode, we will cool down.\n") diff --git a/utilities/tcpinfo_other.go b/utilities/tcpinfo_other.go deleted file mode 100644 index 8dd070b..0000000 --- a/utilities/tcpinfo_other.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd -// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd - -package utilities - -import ( - "net" - - "golang.org/x/sys/unix" -) - -func GetTCPInfo(connection net.Conn) (*unix.TCPInfo, error) { - return nil, NotImplemented{Functionality: "GetTCPInfo"} -} - -func PrintTCPInfo(info *unix.TCPInfo) { - return -} diff --git a/utilities/tcpinfo_unix.go b/utilities/tcpinfo_unix.go deleted file mode 100644 index b0b5252..0000000 --- a/utilities/tcpinfo_unix.go +++ /dev/null @@ -1,62 +0,0 @@ -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd -// +build darwin dragonfly freebsd linux netbsd openbsd - -package utilities - -import ( - "fmt" - "net" - - "golang.org/x/sys/unix" -) - -func GetTCPInfo(connection net.Conn) (*unix.TCPInfo, error) { - tcpConn, ok := connection.(*net.TCPConn) - if !ok { - return nil, fmt.Errorf("connection is not a net.TCPConn") - } - rawConn, err := tcpConn.SyscallConn() - if err != nil { - return nil, err - } - - var info *unix.TCPInfo = nil - rawConn.Control(func(fd uintptr) { - info, err = unix.GetsockoptTCPInfo(int(fd), unix.SOL_TCP, unix.TCP_INFO) - }) - return info, err -} - -func PrintTCPInfo(info *unix.TCPInfo) { - fmt.Printf("TCPInfo: \n") - fmt.Printf(" State: %v\n", info.State) - fmt.Printf(" Ca_state: %v\n", info.Ca_state) - fmt.Printf(" Retransmits: %v\n", info.Retransmits) - fmt.Printf(" Probes: %v\n", info.Probes) - fmt.Printf(" Backoff: %v\n", info.Backoff) - fmt.Printf(" Options: %v\n", info.Options) - fmt.Printf(" Rto: %v\n", info.Rto) - fmt.Printf(" Ato: %v\n", info.Ato) - fmt.Printf(" Snd_mss: %v\n", info.Snd_mss) - fmt.Printf(" Rcv_mss: %v\n", info.Rcv_mss) - fmt.Printf(" Unacked: %v\n", info.Unacked) - fmt.Printf(" Sacked: %v\n", info.Sacked) - fmt.Printf(" Lost: %v\n", info.Lost) - fmt.Printf(" Retrans: %v\n", info.Retrans) - fmt.Printf(" Fackets: %v\n", info.Fackets) - fmt.Printf(" Last_data_sent: %v\n", info.Last_data_sent) - fmt.Printf(" Last_ack_sent: %v\n", info.Last_ack_sent) - fmt.Printf(" Last_data_recv: %v\n", info.Last_data_recv) - fmt.Printf(" Last_ack_recv: %v\n", info.Last_ack_recv) - fmt.Printf(" Pmtu: %v\n", info.Pmtu) - fmt.Printf(" Rcv_ssthresh: %v\n", info.Rcv_ssthresh) - fmt.Printf(" Rtt: %v\n", info.Rtt) - fmt.Printf(" Rttvar: %v\n", info.Rttvar) - fmt.Printf(" Snd_ssthresh: %v\n", info.Snd_ssthresh) - fmt.Printf(" Snd_cwnd: %v\n", info.Snd_cwnd) - fmt.Printf(" Advmss: %v\n", info.Advmss) - fmt.Printf(" Reordering: %v\n", info.Reordering) - fmt.Printf(" Rcv_rtt: %v\n", info.Rcv_rtt) - fmt.Printf(" Rcv_space: %v\n", info.Rcv_space) - fmt.Printf(" Total_retrans: %v\n", info.Total_retrans) -} diff --git a/utilities/utilities.go b/utilities/utilities.go index 7e26ab9..76acbd2 100644 --- a/utilities/utilities.go +++ b/utilities/utilities.go @@ -123,10 +123,9 @@ func RandBetween(max int) int { return rand.New(rand.NewSource(int64(time.Now().Nanosecond()))).Int() % max } -type NotImplemented struct { - Functionality string -} - -func (ni *NotImplemented) Error() string { - return fmt.Sprintf("%v not implemented.\n", ni.Functionality) +func Max(x, y uint64) uint64 { + if x > y { + return x + } + return y } |
