/* * * Copyright 2017 gRPC authors. * * Licensed 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 * * http://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. * */ #include #include "src/core/lib/debug/stats.h" #include #include #include #include #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" #include #include namespace grpc_core { Stats* const g_stats_data = [] { size_t num_cores = gpr_cpu_num_cores(); Stats* stats = static_cast( gpr_zalloc(sizeof(Stats) + num_cores * sizeof(grpc_stats_data))); stats->num_cores = num_cores; return stats; }(); } // namespace grpc_core void grpc_stats_collect(grpc_stats_data* output) { memset(output, 0, sizeof(*output)); for (size_t core = 0; core < grpc_core::g_stats_data->num_cores; core++) { for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { output->counters[i] += gpr_atm_no_barrier_load( &grpc_core::g_stats_data->per_cpu[core].counters[i]); } for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { output->histograms[i] += gpr_atm_no_barrier_load( &grpc_core::g_stats_data->per_cpu[core].histograms[i]); } } } void grpc_stats_diff(const grpc_stats_data* b, const grpc_stats_data* a, grpc_stats_data* c) { for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { c->counters[i] = b->counters[i] - a->counters[i]; } for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { c->histograms[i] = b->histograms[i] - a->histograms[i]; } } void grpc_stats_inc_histogram_value(int histogram, int value) { const int bucket = grpc_stats_get_bucket[histogram](value); gpr_atm_no_barrier_fetch_add( &GRPC_THREAD_STATS_DATA() ->histograms[grpc_stats_histo_start[histogram] + bucket], 1); } size_t grpc_stats_histo_count(const grpc_stats_data* stats, grpc_stats_histograms histogram) { size_t sum = 0; for (int i = 0; i < grpc_stats_histo_buckets[histogram]; i++) { sum += static_cast( stats->histograms[grpc_stats_histo_start[histogram] + i]); } return sum; } static double threshold_for_count_below(const gpr_atm* bucket_counts, const int* bucket_boundaries, int num_buckets, double count_below) { double count_so_far; double lower_bound; double upper_bound; int lower_idx; int upper_idx; /* find the lowest bucket that gets us above count_below */ count_so_far = 0.0; for (lower_idx = 0; lower_idx < num_buckets; lower_idx++) { count_so_far += static_cast(bucket_counts[lower_idx]); if (count_so_far >= count_below) { break; } } if (count_so_far == count_below) { /* this bucket hits the threshold exactly... we should be midway through any run of zero values following the bucket */ for (upper_idx = lower_idx + 1; upper_idx < num_buckets; upper_idx++) { if (bucket_counts[upper_idx]) { break; } } return (bucket_boundaries[lower_idx] + bucket_boundaries[upper_idx]) / 2.0; } else { /* treat values as uniform throughout the bucket, and find where this value should lie */ lower_bound = bucket_boundaries[lower_idx]; upper_bound = bucket_boundaries[lower_idx + 1]; return upper_bound - (upper_bound - lower_bound) * (count_so_far - count_below) / static_cast(bucket_counts[lower_idx]); } } double grpc_stats_histo_percentile(const grpc_stats_data* stats, grpc_stats_histograms histogram, double percentile) { size_t count = grpc_stats_histo_count(stats, histogram); if (count == 0) return 0.0; return threshold_for_count_below( stats->histograms + grpc_stats_histo_start[histogram], grpc_stats_histo_bucket_boundaries[histogram], grpc_stats_histo_buckets[histogram], static_cast(count) * percentile / 100.0); } namespace { template std::string ArrayToJson(const I* values, size_t count) { std::vector parts; for (size_t i = 0; i < count; i++) { parts.push_back(absl::StrFormat("%d", values[i])); } return absl::StrCat("[", absl::StrJoin(parts, ","), "]"); } } // namespace std::string grpc_stats_data_as_json(const grpc_stats_data* data) { std::vector parts; for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { parts.push_back(absl::StrFormat( "\"%s\": %" PRIdPTR, grpc_stats_counter_name[i], data->counters[i])); } for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) { parts.push_back(absl::StrFormat( "\"%s\": %s", grpc_stats_histogram_name[i], ArrayToJson(data->histograms + grpc_stats_histo_start[i], grpc_stats_histo_buckets[i]))); parts.push_back( absl::StrFormat("\"%s_bkt\": %s", grpc_stats_histogram_name[i], ArrayToJson(grpc_stats_histo_bucket_boundaries[i], grpc_stats_histo_buckets[i]))); } return absl::StrCat("{", absl::StrJoin(parts, ", "), "}"); }