diff --git a/include/quicly.h b/include/quicly.h index 900b86dcd..afbf2c6d4 100644 --- a/include/quicly.h +++ b/include/quicly.h @@ -357,6 +357,30 @@ struct st_quicly_conn_streamgroup_state_t { * Total number of packets for which acknowledgements were received after being marked lost. \ */ \ uint64_t late_acked; \ + /** \ + * Total number of ack-only packets received. \ + */ \ + uint64_t ack_only_received; \ + /** \ + * Total number of ack-only packets sent. (TODO) \ + */ \ + uint64_t ack_only_sent; \ + /** \ + * Total number of packets received during the handshake. \ + */ \ + uint64_t handshake_received; \ + /** \ + * Total number of packets sent during the handshake. \ + */ \ + uint64_t handshake_sent; \ + /** \ + * Total number of 0-RTT packets received. \ + */ \ + uint64_t zerortt_received; \ + /** \ + * Total number of 0-RTT packets sent. \ + */ \ + uint64_t zerortt_sent; \ } num_packets; \ struct { \ /** \ @@ -367,9 +391,17 @@ struct st_quicly_conn_streamgroup_state_t { * Total bytes sent, at UDP datagram-level. \ */ \ uint64_t sent; \ + /** \ + * Total stream bytes received. \ + */ \ + uint64_t stream_received; \ + /** \ + * Total stream bytes sent. \ + */ \ + uint64_t stream_sent; \ } num_bytes; \ /** \ - * Total number of PTOs during the connections. \ + * Total number of PTOs during the connection. \ */ \ uint32_t num_ptos @@ -386,6 +418,32 @@ typedef struct st_quicly_stats_t { * Congestion control stats (experimental; TODO cherry-pick what can be exposed as part of a stable API). */ quicly_cc_t cc; + /** + * Connection duration. + */ + uint64_t duration; + /** + * QUIC version. + */ + uint32_t version; + /** + * Maximum UDP payload size used. + */ + uint16_t max_udp_payload_size; + /** + * Number of allowed and opened streams of each type, for locally created streams. + */ + quicly_stream_id_t allowed_local_uni_streams; + quicly_stream_id_t opened_local_uni_streams; + quicly_stream_id_t allowed_local_bidi_streams; + quicly_stream_id_t opened_local_bidi_streams; + /** + * Number of allowed and opened streams of each type, for streams created by remote endpoint. + */ + quicly_stream_id_t allowed_remote_uni_streams; + quicly_stream_id_t opened_remote_uni_streams; + quicly_stream_id_t allowed_remote_bidi_streams; + quicly_stream_id_t opened_remote_bidi_streams; } quicly_stats_t; /** diff --git a/lib/quicly.c b/lib/quicly.c index c69db54a8..a74bc5612 100644 --- a/lib/quicly.c +++ b/lib/quicly.c @@ -411,6 +411,10 @@ struct st_quicly_conn_t { } active_acked_cache; } on_ack_stream; } stash; + /** + * Start time of the connection. + */ + int64_t conn_start_at; }; struct st_quicly_handle_payload_state_t { @@ -1097,6 +1101,18 @@ int quicly_get_stats(quicly_conn_t *conn, quicly_stats_t *stats) /* set or generate the non-pre-built stats fields here */ stats->rtt = conn->egress.loss.rtt; stats->cc = conn->egress.cc; + stats->duration = conn->stash.now - conn->conn_start_at; + stats->version = conn->super.version; + stats->max_udp_payload_size = conn->egress.max_udp_payload_size; + + stats->allowed_local_uni_streams = conn->egress.max_streams.uni.count; + stats->opened_local_uni_streams = conn->super.local.uni.next_stream_id / 4; + stats->allowed_local_bidi_streams = conn->egress.max_streams.bidi.count; + stats->opened_local_bidi_streams = conn->super.local.bidi.next_stream_id / 4; + stats->allowed_remote_uni_streams = conn->ingress.max_streams.uni.max_committed; + stats->opened_remote_uni_streams = conn->super.remote.uni.next_stream_id / 4; + stats->allowed_remote_bidi_streams = conn->ingress.max_streams.bidi.max_committed; + stats->opened_remote_bidi_streams = conn->super.remote.bidi.next_stream_id / 4; return 0; } @@ -1253,6 +1269,13 @@ static int record_receipt(quicly_conn_t *conn, struct st_quicly_pn_space_t *spac conn->egress.send_ack_at = conn->stash.now + QUICLY_DELAYED_ACK_TIMEOUT; } + if (is_ack_only) + ++conn->super.stats.num_packets.ack_only_received; + if (epoch == QUICLY_EPOCH_INITIAL || epoch == QUICLY_EPOCH_HANDSHAKE) + ++conn->super.stats.num_packets.handshake_received; + if (epoch == QUICLY_EPOCH_0RTT) + ++conn->super.stats.num_packets.zerortt_received; + ret = 0; Exit: return ret; @@ -1865,6 +1888,7 @@ static quicly_conn_t *create_connection(quicly_context_t *ctx, const char *serve memset(conn, 0, sizeof(*conn)); conn->super.ctx = ctx; lock_now(conn, 0); + conn->conn_start_at = conn->stash.now; set_address(&conn->super.local.address, local_addr); set_address(&conn->super.remote.address, remote_addr); quicly_local_cid_init_set(&conn->super.local.cid_set, ctx->cid_encryptor, local_cid); @@ -2781,6 +2805,10 @@ static int commit_send_packet(quicly_conn_t *conn, quicly_send_context_t *s, enu ++conn->egress.packet_number; ++conn->super.stats.num_packets.sent; + if (get_epoch(*s->target.first_byte_at) == QUICLY_EPOCH_INITIAL || get_epoch(*s->target.first_byte_at) == QUICLY_EPOCH_HANDSHAKE) + ++conn->super.stats.num_packets.handshake_sent; + if (get_epoch(*s->target.first_byte_at) == QUICLY_EPOCH_0RTT) + ++conn->super.stats.num_packets.zerortt_sent; if (mode != QUICLY_COMMIT_SEND_PACKET_MODE_COALESCED) { conn->super.stats.num_bytes.sent += datagram_size; @@ -3198,6 +3226,7 @@ int quicly_send_stream(quicly_stream_t *stream, quicly_send_context_t *s) UpdateState: QUICLY_PROBE(STREAM_SEND, stream->conn, stream->conn->stash.now, stream, off, end_off - off, is_fin); QUICLY_PROBE(QUICTRACE_SEND_STREAM, stream->conn, stream->conn->stash.now, stream, off, end_off - off, is_fin); + stream->conn->super.stats.num_bytes.stream_sent += end_off - off; /* update sendstate (and also MAX_DATA counter) */ if (stream->sendstate.size_inflight < end_off) { if (stream->stream_id >= 0) @@ -4278,6 +4307,7 @@ static int handle_stream_frame(quicly_conn_t *conn, struct st_quicly_handle_payl if ((ret = quicly_decode_stream_frame(state->frame_type, &state->src, state->end, &frame)) != 0) return ret; QUICLY_PROBE(QUICTRACE_RECV_STREAM, conn, conn->stash.now, frame.stream_id, frame.offset, frame.data.len, (int)frame.is_fin); + conn->super.stats.num_bytes.stream_received += frame.data.len; if ((ret = get_stream_or_open_if_new(conn, frame.stream_id, &stream)) != 0 || stream == NULL) return ret; return apply_stream_frame(stream, &frame); diff --git a/misc/probe2trace.pl b/misc/probe2trace.pl index 2c5d2e26f..4fdd1153e 100755 --- a/misc/probe2trace.pl +++ b/misc/probe2trace.pl @@ -121,20 +121,38 @@ push @fmt, map {qq("rtt_$_":\%u)} qw(minimum smoothed latest); push @fmt, map {qq("cc_$_":\%u)} qw(type cwnd ssthresh cwnd_initial cwnd_exiting_slow_start cwnd_minimum cwnd_maximum num_loss_episodes); push @fmt, map {qq("num_packets_$_":\%llu)} qw(sent ack_received lost lost_time_threshold late_acked received decryption_failed); - push @fmt, map {qq("num_bytes_$_":\%llu)} qw(sent received); + push @fmt, map {qq("num_packets_$_":\%llu)} qw(ack_only_received ack_only_sent handshake_received handshake_sent zerortt_received zerortt_sent); + push @fmt, map {qq("num_bytes_$_":\%llu)} qw(sent received stream_sent stream_received); push @fmt, qq("num_ptos":\%u); + push @fmt, qq("duration":\%llu); + push @fmt, qq("version":\%u); + push @fmt, qq("max_udp_payload_size":\%u); + push @fmt, map {qq("allowed_$_":\%llu)} qw(local_uni_streams local_bidi_streams remote_uni_streams remote_bidi_streams); + push @fmt, map {qq("opened_$_":\%llu)} qw(local_uni_streams local_bidi_streams remote_uni_streams remote_bidi_streams); if ($arch eq 'linux') { push @ap, map{"((struct st_quicly_stats_t *)arg$i)->rtt.$_"} qw(minimum smoothed variance); push @ap, map{"((struct st_quicly_stats_t *)arg$i)->cc.$_"} qw(type cwnd ssthresh cwnd_initial cwnd_exiting_slow_start cwnd_minimum cwnd_maximum num_loss_episodes); push @ap, map{"((struct st_quicly_stats_t *)arg$i)->num_packets.$_"} qw(sent ack_received lost lost_time_threshold late_acked received decryption_failed); - push @ap, map{"((struct st_quicly_stats_t *)arg$i)->num_bytes.$_"} qw(sent received); + push @ap, map{"((struct st_quicly_stats_t *)arg$i)->num_packets.$_"} qw(ack_only_received ack_only_sent handshake_received handshake_sent zerortt_received zerortt_sent); + push @ap, map{"((struct st_quicly_stats_t *)arg$i)->num_bytes.$_"} qw(sent received stream_sent stream_received); push @ap, "((struct st_quicly_stats_t *)arg$i)->num_ptos"; + push @ap, "((struct st_quicly_stats_t *)arg$i)->duration"; + push @ap, "((struct st_quicly_stats_t *)arg$i)->version"; + push @ap, "((struct st_quicly_stats_t *)arg$i)->max_udp_payload_size"; + push @ap, map{"((struct st_quicly_stats_t *)arg$i)->allowed_$_"} qw(local_uni_streams local_bidi_streams remote_uni_streams remote_bidi_streams); + push @ap, map{"((struct st_quicly_stats_t *)arg$i)->opened_$_"} qw(local_uni_streams local_bidi_streams remote_uni_streams remote_bidi_streams); } else { push @ap, map{"arg${i}->rtt.$_"} qw(minimum smoothed variance); push @ap, map{"arg${i}->cc.$_"} qw(type cwnd ssthresh cwnd_initial cwnd_exiting_slow_start cwnd_minimum cwnd_maximum num_loss_episodes); push @ap, map{"(unsigned long long)arg${i}->num_packets.$_"} qw(sent ack_received lost lost_time_threshold late_acked received decryption_failed); - push @ap, map{"(unsigned long long)arg${i}->num_bytes.$_"} qw(sent received); + push @ap, map{"(unsigned long long)arg${i}->num_packets.$_"} qw(ack_only_received ack_only_sent handshake_received handshake_sent zerortt_received zerortt_sent); + push @ap, map{"(unsigned long long)arg${i}->num_bytes.$_"} qw(sent received stream_sent stream_received); push @ap, "arg${i}->num_ptos"; + push @ap, "(unsigned long long)arg${i}->duration"; + push @ap, "arg${i}->version"; + push @ap, "arg${i}->max_udp_payload_size"; + push @ap, map{"(unsigned long long)arg${i}->allowed_$_"} qw(local_uni_streams local_bidi_streams remote_uni_streams remote_bidi_streams); + push @ap, map{"(unsigned long long)arg${i}->opened_$_"} qw(local_uni_streams local_bidi_streams remote_uni_streams remote_bidi_streams); } } else { $name = 'time'