client-stats.go | 10 ++++++++++ client.go | 24 +++++++++++++++++------- peer.go | 2 +- stats.go | 12 ++++++++++++ torrent.go | 13 +++++++++---- torrent_stats.go => torrent-stats.go | 0 diff --git a/client-stats.go b/client-stats.go new file mode 100644 index 0000000000000000000000000000000000000000..d799bae6bb86f12816d923c7e11809b8d72f1e90 --- /dev/null +++ b/client-stats.go @@ -0,0 +1,10 @@ +package torrent + +type ClientStats struct { + ConnStats + + // Ongoing outgoing dial attempts. There may be more than one dial going on per peer address due + // to hole-punch connect requests. The total may not match the sum of attempts for all Torrents + // if a Torrent is dropped while there are outstanding dials. + ActiveHalfOpenAttempts int +} diff --git a/client.go b/client.go index 709bc4c07ca6033a20ecbb33e4b0697b371b939f..65545973b8733db2c46d81a8127196fdec57f89a 100644 --- a/client.go +++ b/client.go @@ -57,7 +57,7 @@ // TCP/UDP protocol ports, and DHT as desired. type Client struct { // An aggregate of stats over all connections. First in struct to ensure 64-bit alignment of // fields. See #262. - stats ConnStats + connStats ConnStats _mu lockWithDeferreds event sync.Cond @@ -149,7 +149,7 @@ cl.eachDhtServer(func(s DhtServer) { fmt.Fprintf(w, "%s DHT server at %s:\n", s.Addr().Network(), s.Addr().String()) writeDhtServerStatus(w, s) }) - spew.Fdump(w, &cl.stats) + dumpStats(w, cl.statsLocked()) torrentsSlice := cl.torrentsAsSlice() fmt.Fprintf(w, "# Torrents: %d\n", len(torrentsSlice)) fmt.Fprintln(w) @@ -694,9 +694,7 @@ } func (cl *Client) countHalfOpenFromTorrents() (count int) { for _, t := range cl.torrents { - for _, attempts := range t.halfOpen { - count += len(attempts) - } + count += t.numHalfOpenAttempts() } return } @@ -1783,8 +1781,20 @@ func (cl *Client) String() string { return fmt.Sprintf("<%[1]T %[1]p>", cl) } -// Returns connection-level aggregate stats at the Client level. See the comment on +// Returns connection-level aggregate connStats at the Client level. See the comment on // TorrentStats.ConnStats. func (cl *Client) ConnStats() ConnStats { - return cl.stats.Copy() + return cl.connStats.Copy() +} + +func (cl *Client) Stats() ClientStats { + cl.rLock() + defer cl.rUnlock() + return cl.statsLocked() +} + +func (cl *Client) statsLocked() (stats ClientStats) { + stats.ConnStats = cl.connStats.Copy() + stats.ActiveHalfOpenAttempts = cl.numHalfOpen + return } diff --git a/peer.go b/peer.go index 1d8ead1002b49758b0564ac47516d9f6fc420c80..4f7831373e2f07c852411db796c014d2523af92b 100644 --- a/peer.go +++ b/peer.go @@ -519,7 +519,7 @@ // connection. func (cn *Peer) postHandshakeStats(f func(*ConnStats)) { t := cn.t f(&t.stats) - f(&t.cl.stats) + f(&t.cl.connStats) } // All ConnStats that include this connection. Some objects are not known diff --git a/stats.go b/stats.go new file mode 100644 index 0000000000000000000000000000000000000000..90144bf7b2f7cdbb0050938d8ce10dd47ffb8385 --- /dev/null +++ b/stats.go @@ -0,0 +1,12 @@ +package torrent + +import ( + "io" + + "github.com/davecgh/go-spew/spew" +) + +func dumpStats[T any](w io.Writer, stats T) { + spew.NewDefaultConfig() + spew.Fdump(w, stats) +} diff --git a/torrent.go b/torrent.go index 41848e4a2a39662cd619e0387182d392ce85410f..7ad67b2b4f92025a9aaf04136421213f62fe629f 100644 --- a/torrent.go +++ b/torrent.go @@ -31,7 +31,6 @@ "github.com/anacrolix/missinggo/v2/bitmap" "github.com/anacrolix/missinggo/v2/pubsub" "github.com/anacrolix/multiless" "github.com/anacrolix/sync" - "github.com/davecgh/go-spew/spew" "github.com/pion/datachannel" "golang.org/x/exp/maps" @@ -767,8 +766,7 @@ }() fmt.Fprintf(w, "DHT Announces: %d\n", t.numDHTAnnounces) - spew.NewDefaultConfig() - spew.Fdump(w, t.statsLocked()) + dumpStats(w, t.statsLocked()) fmt.Fprintf(w, "webseeds:\n") t.writePeerStatuses(w, maps.Values(t.webSeeds)) @@ -2455,7 +2453,7 @@ // All stats that include this Torrent. Useful when we want to increment ConnStats but not for every // connection. func (t *Torrent) allStats(f func(*ConnStats)) { f(&t.stats) - f(&t.cl.stats) + f(&t.cl.connStats) } func (t *Torrent) hashingPiece(i pieceIndex) bool { @@ -2859,3 +2857,10 @@ panic("expected to fail earlier if rendezvous already exists") } return } + +func (t *Torrent) numHalfOpenAttempts() (num int) { + for _, attempts := range t.halfOpen { + num += len(attempts) + } + return +} diff --git a/torrent_stats.go b/torrent-stats.go rename from torrent_stats.go rename to torrent-stats.go