Record some stats from QUIC TlsServerHandshaker:
- The result of select cert/compute signature/decrypt ticket operations.
- The latencies of the operations above, if they are done asynchronously.

PiperOrigin-RevId: 363019712
Change-Id: I7dc8f867183a9ef5110d6818f0c9b547905bbfef
diff --git a/quic/core/quic_connection_stats.h b/quic/core/quic_connection_stats.h
index 0cf3489..06b7f58 100644
--- a/quic/core/quic_connection_stats.h
+++ b/quic/core/quic_connection_stats.h
@@ -209,6 +209,21 @@
   // which was canceled because the peer migrated again. Such migration is also
   // counted as invalid peer migration.
   size_t num_peer_migration_while_validating_default_path = 0;
+
+  struct QUIC_NO_EXPORT TlsServerOperationStats {
+    bool success = false;
+    // If the operation is performed asynchronously, how long did it take.
+    // Zero() for synchronous operations.
+    QuicTime::Delta async_latency = QuicTime::Delta::Zero();
+  };
+
+  // The TLS server op stats only have values when the corresponding operation
+  // is performed by TlsServerHandshaker. If an operation is done within
+  // BoringSSL, e.g. ticket decrypted without using
+  // TlsServerHandshaker::SessionTicketOpen, it will not be recorded here.
+  absl::optional<TlsServerOperationStats> tls_server_select_cert_stats;
+  absl::optional<TlsServerOperationStats> tls_server_compute_signature_stats;
+  absl::optional<TlsServerOperationStats> tls_server_decrypt_ticket_stats;
 };
 
 }  // namespace quic
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index e581c7a..b6a7d80 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -21,8 +21,17 @@
 #include "quic/platform/api/quic_flags.h"
 #include "quic/platform/api/quic_hostname_utils.h"
 #include "quic/platform/api/quic_logging.h"
+#include "quic/platform/api/quic_server_stats.h"
 #include "common/platform/api/quiche_text_utils.h"
 
+#define RECORD_LATENCY_IN_US(stat_name, latency, comment)                   \
+  do {                                                                      \
+    const int64_t latency_in_us = (latency).ToMicroseconds();               \
+    QUIC_DVLOG(1) << "Recording " stat_name ": " << latency_in_us;          \
+    QUIC_SERVER_HISTOGRAM_COUNTS(stat_name, latency_in_us, 1, 10000000, 50, \
+                                 comment);                                  \
+  } while (0)
+
 namespace quic {
 
 TlsServerHandshaker::DefaultProofSourceHandle::DefaultProofSourceHandle(
@@ -597,6 +606,12 @@
         sig_alg, in, max_out);
     if (status == QUIC_PENDING) {
       set_expected_ssl_error(SSL_ERROR_WANT_PRIVATE_KEY_OPERATION);
+      if (async_op_timer_.has_value()) {
+        QUIC_CODE_COUNT(
+            quic_tls_server_computing_signature_while_another_op_pending);
+      }
+      async_op_timer_ = QuicTimeAccumulator();
+      async_op_timer_->Start(now());
     }
     return PrivateKeyComplete(out, out_len, max_out);
   }
@@ -620,7 +635,23 @@
   if (expected_ssl_error() == SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) {
     return ssl_private_key_retry;
   }
-  if (!HasValidSignature(max_out)) {
+
+  const bool success = HasValidSignature(max_out);
+  QuicConnectionStats::TlsServerOperationStats compute_signature_stats;
+  compute_signature_stats.success = success;
+  if (async_op_timer_.has_value()) {
+    async_op_timer_->Stop(now());
+    compute_signature_stats.async_latency =
+        async_op_timer_->GetTotalElapsedTime();
+    async_op_timer_.reset();
+    RECORD_LATENCY_IN_US("tls_server_async_compute_signature_latency_us",
+                         compute_signature_stats.async_latency,
+                         "Async compute signature latency in microseconds");
+  }
+  connection_stats().tls_server_compute_signature_stats =
+      std::move(compute_signature_stats);
+
+  if (!success) {
     return ssl_private_key_failure;
   }
   *out_len = cert_verify_sig_.size();
@@ -698,14 +729,44 @@
     // returning ssl_ticket_aead_retry, we should continue processing to return
     // the decrypted ticket.
     //
-    // If the callback is not run asynchronously, return ssl_ticket_aead_retry
+    // If the callback is not run synchronously, return ssl_ticket_aead_retry
     // and when the callback is complete this function will be run again to
     // return the result.
     if (ticket_decryption_callback_) {
       set_expected_ssl_error(SSL_ERROR_PENDING_TICKET);
+      if (async_op_timer_.has_value()) {
+        QUIC_CODE_COUNT(
+            quic_tls_server_decrypting_ticket_while_another_op_pending);
+      }
+      async_op_timer_ = QuicTimeAccumulator();
+      async_op_timer_->Start(now());
       return ssl_ticket_aead_retry;
     }
   }
+
+  ssl_ticket_aead_result_t result =
+      FinalizeSessionTicketOpen(out, out_len, max_out_len);
+
+  QuicConnectionStats::TlsServerOperationStats decrypt_ticket_stats;
+  decrypt_ticket_stats.success = (result == ssl_ticket_aead_success);
+  if (async_op_timer_.has_value()) {
+    async_op_timer_->Stop(now());
+    decrypt_ticket_stats.async_latency = async_op_timer_->GetTotalElapsedTime();
+    async_op_timer_.reset();
+    RECORD_LATENCY_IN_US("tls_server_async_decrypt_ticket_latency_us",
+                         decrypt_ticket_stats.async_latency,
+                         "Async decrypt ticket latency in microseconds");
+  }
+  connection_stats().tls_server_compute_signature_stats =
+      std::move(decrypt_ticket_stats);
+
+  return result;
+}
+
+ssl_ticket_aead_result_t TlsServerHandshaker::FinalizeSessionTicketOpen(
+    uint8_t* out,
+    size_t* out_len,
+    size_t max_out_len) {
   ticket_decryption_callback_ = nullptr;
   set_expected_ssl_error(SSL_ERROR_WANT_READ);
   if (decrypted_session_ticket_.empty()) {
@@ -807,6 +868,12 @@
 
     if (status == QUIC_PENDING) {
       set_expected_ssl_error(SSL_ERROR_PENDING_CERTIFICATE);
+      if (async_op_timer_.has_value()) {
+        QUIC_CODE_COUNT(
+            quic_tls_server_selecting_cert_while_another_op_pending);
+      }
+      async_op_timer_ = QuicTimeAccumulator();
+      async_op_timer_->Start(now());
       return ssl_select_cert_retry;
     }
 
@@ -865,6 +932,21 @@
       QUIC_LOG(ERROR) << "No certs provided for host '" << hostname_ << "'";
     }
   }
+
+  QuicConnectionStats::TlsServerOperationStats select_cert_stats;
+  select_cert_stats.success = (select_cert_status_ == QUIC_SUCCESS);
+  QUICHE_DCHECK_NE(is_sync, async_op_timer_.has_value());
+  if (async_op_timer_.has_value()) {
+    async_op_timer_->Stop(now());
+    select_cert_stats.async_latency = async_op_timer_->GetTotalElapsedTime();
+    async_op_timer_.reset();
+    RECORD_LATENCY_IN_US("tls_server_async_select_cert_latency_us",
+                         select_cert_stats.async_latency,
+                         "Async select cert latency in microseconds");
+  }
+  connection_stats().tls_server_select_cert_stats =
+      std::move(select_cert_stats);
+
   const int last_expected_ssl_error = expected_ssl_error();
   set_expected_ssl_error(SSL_ERROR_WANT_READ);
   if (!is_sync) {
diff --git a/quic/core/tls_server_handshaker.h b/quic/core/tls_server_handshaker.h
index 16707cd..7850d35 100644
--- a/quic/core/tls_server_handshaker.h
+++ b/quic/core/tls_server_handshaker.h
@@ -15,6 +15,7 @@
 #include "quic/core/proto/cached_network_parameters_proto.h"
 #include "quic/core/quic_crypto_server_stream_base.h"
 #include "quic/core/quic_crypto_stream.h"
+#include "quic/core/quic_time_accumulator.h"
 #include "quic/core/quic_types.h"
 #include "quic/core/tls_handshaker.h"
 #include "quic/platform/api/quic_export.h"
@@ -155,6 +156,11 @@
                                              size_t* out_len,
                                              size_t max_out_len,
                                              absl::string_view in) override;
+  // Called when ticket_decryption_callback_ is done to determine a final
+  // decryption result.
+  ssl_ticket_aead_result_t FinalizeSessionTicketOpen(uint8_t* out,
+                                                     size_t* out_len,
+                                                     size_t max_out_len);
   TlsConnection::Delegate* ConnectionDelegate() override { return this; }
 
   // The status of cert selection. nullopt means it hasn't started.
@@ -296,6 +302,11 @@
   bool ProcessTransportParameters(const SSL_CLIENT_HELLO* client_hello,
                                   std::string* error_details);
 
+  QuicConnectionStats& connection_stats() {
+    return session()->connection()->mutable_stats();
+  }
+  QuicTime now() const { return session()->GetClock()->Now(); }
+
   std::unique_ptr<ProofSourceHandle> proof_source_handle_;
   ProofSource* proof_source_;
   SignatureCallback* signature_callback_ = nullptr;
@@ -321,6 +332,9 @@
   std::string cert_verify_sig_;
   std::unique_ptr<ProofSource::Details> proof_source_details_;
 
+  // Count the duration of the current async operation, if any.
+  absl::optional<QuicTimeAccumulator> async_op_timer_;
+
   std::unique_ptr<ApplicationState> application_state_;
 
   // Pre-shared key used during the handshake.