|  | @@ -933,6 +933,11 @@ typedef struct client_channel_call_data {
 | 
	
		
			
				|  |  |    grpc_closure pick_closure;
 | 
	
		
			
				|  |  |    grpc_closure pick_cancel_closure;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  // state needed to support channelz interception of recv trailing metadata.
 | 
	
		
			
				|  |  | +  grpc_closure recv_trailing_metadata_ready_channelz;
 | 
	
		
			
				|  |  | +  grpc_closure* original_recv_trailing_metadata;
 | 
	
		
			
				|  |  | +  grpc_metadata_batch* recv_trailing_metadata;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    grpc_polling_entity* pollent;
 | 
	
		
			
				|  |  |    bool pollent_added_to_interested_parties;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -994,6 +999,8 @@ static void start_internal_recv_trailing_metadata(grpc_call_element* elem);
 | 
	
		
			
				|  |  |  static void on_complete(void* arg, grpc_error* error);
 | 
	
		
			
				|  |  |  static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored);
 | 
	
		
			
				|  |  |  static void start_pick_locked(void* arg, grpc_error* ignored);
 | 
	
		
			
				|  |  | +static void maybe_intercept_recv_trailing_metadata_for_channelz(
 | 
	
		
			
				|  |  | +    grpc_call_element* elem, grpc_transport_stream_op_batch* batch);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  // send op data caching
 | 
	
	
		
			
				|  | @@ -1292,6 +1299,7 @@ static void pending_batches_resume(grpc_call_element* elem) {
 | 
	
		
			
				|  |  |      pending_batch* pending = &calld->pending_batches[i];
 | 
	
		
			
				|  |  |      grpc_transport_stream_op_batch* batch = pending->batch;
 | 
	
		
			
				|  |  |      if (batch != nullptr) {
 | 
	
		
			
				|  |  | +      maybe_intercept_recv_trailing_metadata_for_channelz(elem, batch);
 | 
	
		
			
				|  |  |        batch->handler_private.extra_arg = calld->subchannel_call;
 | 
	
		
			
				|  |  |        GRPC_CLOSURE_INIT(&batch->handler_private.closure,
 | 
	
		
			
				|  |  |                          resume_pending_batch_in_call_combiner, batch,
 | 
	
	
		
			
				|  | @@ -1777,23 +1785,22 @@ static void recv_message_ready(void* arg, grpc_error* error) {
 | 
	
		
			
				|  |  |  // recv_trailing_metadata handling
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Sets *status and *server_pushback_md based on batch_data and error.
 | 
	
		
			
				|  |  | -static void get_call_status(subchannel_batch_data* batch_data,
 | 
	
		
			
				|  |  | -                            grpc_error* error, grpc_status_code* status,
 | 
	
		
			
				|  |  | +// Sets *status and *server_pushback_md based on md_batch and error.
 | 
	
		
			
				|  |  | +// Only sets *server_pushback_md if server_pushback_md != nullptr.
 | 
	
		
			
				|  |  | +static void get_call_status(grpc_call_element* elem,
 | 
	
		
			
				|  |  | +                            grpc_metadata_batch* md_batch, grpc_error* error,
 | 
	
		
			
				|  |  | +                            grpc_status_code* status,
 | 
	
		
			
				|  |  |                              grpc_mdelem** server_pushback_md) {
 | 
	
		
			
				|  |  | -  grpc_call_element* elem = batch_data->elem;
 | 
	
		
			
				|  |  |    call_data* calld = static_cast<call_data*>(elem->call_data);
 | 
	
		
			
				|  |  |    if (error != GRPC_ERROR_NONE) {
 | 
	
		
			
				|  |  |      grpc_error_get_status(error, calld->deadline, status, nullptr, nullptr,
 | 
	
		
			
				|  |  |                            nullptr);
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  | -    grpc_metadata_batch* md_batch =
 | 
	
		
			
				|  |  | -        batch_data->batch.payload->recv_trailing_metadata
 | 
	
		
			
				|  |  | -            .recv_trailing_metadata;
 | 
	
		
			
				|  |  |      GPR_ASSERT(md_batch->idx.named.grpc_status != nullptr);
 | 
	
		
			
				|  |  |      *status =
 | 
	
		
			
				|  |  |          grpc_get_status_code_from_metadata(md_batch->idx.named.grpc_status->md);
 | 
	
		
			
				|  |  | -    if (md_batch->idx.named.grpc_retry_pushback_ms != nullptr) {
 | 
	
		
			
				|  |  | +    if (server_pushback_md != nullptr &&
 | 
	
		
			
				|  |  | +        md_batch->idx.named.grpc_retry_pushback_ms != nullptr) {
 | 
	
		
			
				|  |  |        *server_pushback_md = &md_batch->idx.named.grpc_retry_pushback_ms->md;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -1966,8 +1973,19 @@ static void recv_trailing_metadata_ready(void* arg, grpc_error* error) {
 | 
	
		
			
				|  |  |    // Get the call's status and check for server pushback metadata.
 | 
	
		
			
				|  |  |    grpc_status_code status = GRPC_STATUS_OK;
 | 
	
		
			
				|  |  |    grpc_mdelem* server_pushback_md = nullptr;
 | 
	
		
			
				|  |  | -  get_call_status(batch_data, GRPC_ERROR_REF(error), &status,
 | 
	
		
			
				|  |  | +  grpc_metadata_batch* md_batch =
 | 
	
		
			
				|  |  | +      batch_data->batch.payload->recv_trailing_metadata.recv_trailing_metadata;
 | 
	
		
			
				|  |  | +  get_call_status(elem, md_batch, GRPC_ERROR_REF(error), &status,
 | 
	
		
			
				|  |  |                    &server_pushback_md);
 | 
	
		
			
				|  |  | +  grpc_core::channelz::SubchannelNode* channelz_subchannel =
 | 
	
		
			
				|  |  | +      calld->pick.connected_subchannel->channelz_subchannel();
 | 
	
		
			
				|  |  | +  if (channelz_subchannel != nullptr) {
 | 
	
		
			
				|  |  | +    if (status == GRPC_STATUS_OK) {
 | 
	
		
			
				|  |  | +      channelz_subchannel->RecordCallSucceeded();
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      channelz_subchannel->RecordCallFailed();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |    if (grpc_client_channel_trace.enabled()) {
 | 
	
		
			
				|  |  |      gpr_log(GPR_INFO, "chand=%p calld=%p: call finished, status=%s", chand,
 | 
	
		
			
				|  |  |              calld, grpc_status_code_to_string(status));
 | 
	
	
		
			
				|  | @@ -2571,6 +2589,69 @@ static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored) {
 | 
	
		
			
				|  |  |    closures.RunClosures(calld->call_combiner);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// Channelz
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void recv_trailing_metadata_ready_channelz(void* arg,
 | 
	
		
			
				|  |  | +                                                  grpc_error* error) {
 | 
	
		
			
				|  |  | +  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
 | 
	
		
			
				|  |  | +  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
 | 
	
		
			
				|  |  | +  call_data* calld = static_cast<call_data*>(elem->call_data);
 | 
	
		
			
				|  |  | +  if (grpc_client_channel_trace.enabled()) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | +            "chand=%p calld=%p: got recv_trailing_metadata_ready_channelz, "
 | 
	
		
			
				|  |  | +            "error=%s",
 | 
	
		
			
				|  |  | +            chand, calld, grpc_error_string(error));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  GPR_ASSERT(calld->recv_trailing_metadata != nullptr);
 | 
	
		
			
				|  |  | +  grpc_status_code status = GRPC_STATUS_OK;
 | 
	
		
			
				|  |  | +  grpc_metadata_batch* md_batch = calld->recv_trailing_metadata;
 | 
	
		
			
				|  |  | +  get_call_status(elem, md_batch, GRPC_ERROR_REF(error), &status, nullptr);
 | 
	
		
			
				|  |  | +  grpc_core::channelz::SubchannelNode* channelz_subchannel =
 | 
	
		
			
				|  |  | +      calld->pick.connected_subchannel->channelz_subchannel();
 | 
	
		
			
				|  |  | +  GPR_ASSERT(channelz_subchannel != nullptr);
 | 
	
		
			
				|  |  | +  if (status == GRPC_STATUS_OK) {
 | 
	
		
			
				|  |  | +    channelz_subchannel->RecordCallSucceeded();
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    channelz_subchannel->RecordCallFailed();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  calld->recv_trailing_metadata = nullptr;
 | 
	
		
			
				|  |  | +  GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata, error);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// If channelz is enabled, intercept recv_trailing so that we may check the
 | 
	
		
			
				|  |  | +// status and associate it to a subchannel.
 | 
	
		
			
				|  |  | +// Returns true if callback was intercepted, false otherwise.
 | 
	
		
			
				|  |  | +static void maybe_intercept_recv_trailing_metadata_for_channelz(
 | 
	
		
			
				|  |  | +    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
 | 
	
		
			
				|  |  | +  call_data* calld = static_cast<call_data*>(elem->call_data);
 | 
	
		
			
				|  |  | +  // only intercept payloads with recv trailing.
 | 
	
		
			
				|  |  | +  if (!batch->recv_trailing_metadata) {
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // only add interceptor is channelz is enabled.
 | 
	
		
			
				|  |  | +  if (calld->pick.connected_subchannel->channelz_subchannel() == nullptr) {
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (grpc_client_channel_trace.enabled()) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | +            "calld=%p batch=%p: intercepting recv trailing for channelz", calld,
 | 
	
		
			
				|  |  | +            batch);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready_channelz,
 | 
	
		
			
				|  |  | +                    recv_trailing_metadata_ready_channelz, elem,
 | 
	
		
			
				|  |  | +                    grpc_schedule_on_exec_ctx);
 | 
	
		
			
				|  |  | +  // save some state needed for the interception callback.
 | 
	
		
			
				|  |  | +  GPR_ASSERT(calld->recv_trailing_metadata == nullptr);
 | 
	
		
			
				|  |  | +  calld->recv_trailing_metadata =
 | 
	
		
			
				|  |  | +      batch->payload->recv_trailing_metadata.recv_trailing_metadata;
 | 
	
		
			
				|  |  | +  calld->original_recv_trailing_metadata =
 | 
	
		
			
				|  |  | +      batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
 | 
	
		
			
				|  |  | +  batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
 | 
	
		
			
				|  |  | +      &calld->recv_trailing_metadata_ready_channelz;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  // LB pick
 | 
	
		
			
				|  |  |  //
 | 
	
	
		
			
				|  | @@ -2600,6 +2681,11 @@ static void create_subchannel_call(grpc_call_element* elem, grpc_error* error) {
 | 
	
		
			
				|  |  |      new_error = grpc_error_add_child(new_error, error);
 | 
	
		
			
				|  |  |      pending_batches_fail(elem, new_error, true /* yield_call_combiner */);
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  | +    grpc_core::channelz::SubchannelNode* channelz_subchannel =
 | 
	
		
			
				|  |  | +        calld->pick.connected_subchannel->channelz_subchannel();
 | 
	
		
			
				|  |  | +    if (channelz_subchannel != nullptr) {
 | 
	
		
			
				|  |  | +      channelz_subchannel->RecordCallStarted();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |      if (parent_data_size > 0) {
 | 
	
		
			
				|  |  |        subchannel_call_retry_state* retry_state =
 | 
	
		
			
				|  |  |            static_cast<subchannel_call_retry_state*>(
 |