Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to get path stats #680

Merged
merged 3 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,23 @@ default Future<QuicConnectionStats> collectStats() {
*/
Future<QuicConnectionStats> collectStats(Promise<QuicConnectionStats> promise);

/**
* Collects statistics about the path of the connection and notifies the {@link Future} once done.
*
* @return the {@link Future} that is notified once the stats were collected.
*/
default Future<QuicConnectionPathStats> collectPathStats(int pathIdx) {
return collectPathStats(pathIdx, eventLoop().newPromise());
}

/**
* Collects statistics about the path of the connection and notifies the {@link Promise} once done.
*
* @param promise the {@link ChannelPromise} that is notified once the stats were collected.
* @return the {@link Future} that is notified once the stats were collected.
*/
Future<QuicConnectionPathStats> collectPathStats(int pathIdx, Promise<QuicConnectionPathStats> promise);

/**
* Creates a new {@link QuicChannelBootstrap} that can be used to create and connect new {@link QuicChannel}s to
* endpoints using the given {@link Channel} as transport layer.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2020 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.incubator.codec.quic;

import java.net.InetSocketAddress;

/**
* Statistics about a path of the {@code QUIC} connection. If unknown by the implementation it might return {@code -1} values
* for the various methods.
*/
public interface QuicConnectionPathStats {
/**
* @return The local address used by this path.
*/
InetSocketAddress localAddress();

/**
* @return The peer address seen by this path.
*/
InetSocketAddress peerAddress();

/**
* @return The validation state of the path.
*/
long validationState();

/**
* @return Whether this path is active.
*/
boolean active();

/**
* @return The number of QUIC packets received on this path.
*/
long recv();

/**
* @return The number of QUIC packets sent on this path.
*/
long sent();

/**
* @return The number of QUIC packets that were lost on this path.
*/
long lost();

/**
* @return The number of sent QUIC packets with retransmitted data on this path.
*/
long retrans();

/**
* @return The estimated round-trip time of the path (in nanoseconds).
*/
long rtt();

/**
* @return The size of the path's congestion window in bytes.
*/
long cwnd();

/**
* @return The number of sent bytes on this path.
*/
long sentBytes();

/**
* @return The number of received bytes on this path.
*/
long recvBytes();

/**
* @return The number of bytes lost on this path.
*/
long lostBytes();

/**
* @return The number of stream bytes retransmitted on this path.
*/
long streamRetransBytes();

/**
* @return The current PMTU for the path.
*/
long pmtu();

/**
* @return The most recent data delivery rate estimate in bytes/s.
*/
long deliveryRate();
}
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,13 @@ static native int quiche_conn_stream_priority(
*/
static native void quiche_stream_iter_free(long iterAddr);


/**
* See
* <a href="https://github.com/cloudflare/quiche/blob/0.20.0/quiche/include/quiche.h#L672">quiche_conn_path_stats</a>.
*/
static native Object[] quiche_conn_path_stats(long connAddr, long streamIdx);

/**
* See
* <a href="https://github.com/cloudflare/quiche/blob/0.6.0/include/quiche.h#L358">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2050,6 +2050,33 @@ private QuicConnectionStats collectStats0(QuicheQuicConnection connection, Promi
return connStats;
}

@Override
public Future<QuicConnectionPathStats> collectPathStats(int pathIdx, Promise<QuicConnectionPathStats> promise) {
if (eventLoop().inEventLoop()) {
collectPathStats0(pathIdx, promise);
} else {
eventLoop().execute(() -> collectPathStats0(pathIdx, promise));
}
return promise;
}

private void collectPathStats0(int pathIdx, Promise<QuicConnectionPathStats> promise) {
collectPathStats0(connection, pathIdx, promise);
}

private QuicConnectionPathStats collectPathStats0(QuicheQuicConnection connection, int pathIdx, Promise<QuicConnectionPathStats> promise) {
final Object[] stats = Quiche.quiche_conn_path_stats(connection.address(), pathIdx);
dries-c marked this conversation as resolved.
Show resolved Hide resolved
if (stats == null) {
promise.setFailure(new IllegalStateException("native quiche_conn_path_stats(...) failed"));
return null;
}

final QuicheQuicConnectionPathStats connStats =
new QuicheQuicConnectionPathStats(stats);
promise.setSuccess(connStats);
return connStats;
}

@Override
public QuicTransportParameters peerTransportParameters() {
return connection.peerParameters();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright 2020 The Netty Project
dries-c marked this conversation as resolved.
Show resolved Hide resolved
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.incubator.codec.quic;

import io.netty.util.internal.StringUtil;

import java.net.InetSocketAddress;

final class QuicheQuicConnectionPathStats implements QuicConnectionPathStats {

private final Object[] values;

QuicheQuicConnectionPathStats(Object[] values) {
this.values = values;
}

@Override
public InetSocketAddress localAddress() {
return (InetSocketAddress) values[0];
}

@Override
public InetSocketAddress peerAddress() {
return (InetSocketAddress) values[1];
}

public long validationState() {
return (long) values[2];
}

@Override
public boolean active() {
return (boolean) values[3];
}

@Override
public long recv() {
return (long) values[4];
}

@Override
public long sent() {
return (long) values[5];
}

@Override
public long lost() {
return (long) values[6];
}

@Override
public long retrans() {
return (long) values[7];
}

@Override
public long rtt() {
return (long) values[8];
}

@Override
public long cwnd() {
return (long) values[9];
}

@Override
public long sentBytes() {
return (long) values[10];
}

@Override
public long recvBytes() {
return (long) values[11];
}

@Override
public long lostBytes() {
return (long) values[12];
}

@Override
public long streamRetransBytes() {
return (long) values[13];
}

@Override
public long pmtu() {
return (long) values[14];
}

@Override
public long deliveryRate() {
return (long) values[15];
}

/**
* Returns the {@link String} representation of stats.
*/
@Override
public String toString() {
return StringUtil.simpleClassName(this) + "[" +
"local=" + localAddress() +
", peer=" + peerAddress() +
", validationState=" + validationState() +
", active=" + active() +
", recv=" + recv() +
", sent=" + sent() +
", lost=" + lost() +
", retrans=" + retrans() +
", rtt=" + rtt() +
", cwnd=" + cwnd() +
", sentBytes=" + sentBytes() +
", recvBytes=" + recvBytes() +
", lostBytes=" + lostBytes() +
", streamRetransBytes=" + streamRetransBytes() +
", pmtu=" + pmtu() +
", deliveryRate=" + deliveryRate() +
']';
}

}
34 changes: 34 additions & 0 deletions codec-native-quic/src/main/c/netty_quic_quiche.c
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,39 @@ static jobject netty_new_socket_address(JNIEnv* env, const struct sockaddr_stora
return (*env)->NewObject(env, inetsocketaddress_class, inetsocketaddress_class_constructor, address, port);
}

static jobjectArray netty_quiche_conn_path_stats(JNIEnv* env, jclass clazz, jlong conn, jlong idx) {
quiche_path_stats stats = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
quiche_conn_path_stats((quiche_conn *) conn, idx, &stats);
dries-c marked this conversation as resolved.
Show resolved Hide resolved
normanmaurer marked this conversation as resolved.
Show resolved Hide resolved

jobject localAddr = netty_new_socket_address(env, &stats.local_addr);
if (localAddr == NULL) {
return NULL;
}
jobject peerAddr = netty_new_socket_address(env, &stats.peer_addr);
if (peerAddr == NULL) {
return NULL;
}

jobjectArray array = (*env)->NewObjectArray(env, 16, object_class, NULL);
(*env)->SetObjectArrayElement(env, array, 0, localAddr);
(*env)->SetObjectArrayElement(env, array, 1, peerAddr);
(*env)->SetObjectArrayElement(env, array, 2, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.validation_state));
(*env)->SetObjectArrayElement(env, array, 3, (*env)->CallStaticObjectMethod(env, boolean_class, boolean_class_valueof, stats.active ? JNI_TRUE : JNI_FALSE));
(*env)->SetObjectArrayElement(env, array, 4, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.recv));
(*env)->SetObjectArrayElement(env, array, 5, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.sent));
(*env)->SetObjectArrayElement(env, array, 6, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.lost));
(*env)->SetObjectArrayElement(env, array, 7, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.retrans));
(*env)->SetObjectArrayElement(env, array, 8, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.rtt));
(*env)->SetObjectArrayElement(env, array, 9, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.cwnd));
(*env)->SetObjectArrayElement(env, array, 10, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.sent_bytes));
(*env)->SetObjectArrayElement(env, array, 11, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.recv_bytes));
(*env)->SetObjectArrayElement(env, array, 12, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.lost_bytes));
(*env)->SetObjectArrayElement(env, array, 13, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.stream_retrans_bytes));
(*env)->SetObjectArrayElement(env, array, 14, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.pmtu));
(*env)->SetObjectArrayElement(env, array, 15, (*env)->CallStaticObjectMethod(env, long_class, long_class_valueof, (jlong) stats.delivery_rate));
return array;
}

static jobjectArray netty_quiche_path_event_new(JNIEnv* env, jclass clazz, jlong ev) {
struct sockaddr_storage local;
socklen_t local_len;
Expand Down Expand Up @@ -1168,6 +1201,7 @@ static const JNINativeMethod fixed_method_table[] = {
{ "sockaddr_cmp", "(JJ)I", (void *) netty_sockaddr_cmp},
{ "quiche_conn_path_event_next", "(J)J", (void *) netty_quiche_conn_path_event_next },
{ "quiche_path_event_type", "(J)I", (void *) netty_quiche_path_event_type },
{ "quiche_conn_path_stats", "(JJ)[Ljava/lang/Object;", (void *) netty_quiche_conn_path_stats },
{ "quiche_path_event_new", "(J)[Ljava/lang/Object;", (void *) netty_quiche_path_event_new },
{ "quiche_path_event_validated", "(J)[Ljava/lang/Object;", (void *) netty_quiche_path_event_validated },
{ "quiche_path_event_failed_validation", "(J)[Ljava/lang/Object;", (void *) netty_quiche_path_event_failed_validation },
Expand Down
Loading