1 /* 2 * 3 * Copyright 2017 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 #include <grpc/support/port_platform.h> 20 21 #include "src/core/lib/debug/stats.h" 22 23 #include <inttypes.h> 24 #include <string.h> 25 26 #include <grpc/support/alloc.h> 27 #include <grpc/support/string_util.h> 28 29 #include "src/core/lib/gpr/string.h" 30 #include "src/core/lib/gpr/useful.h" 31 32 grpc_stats_data* grpc_stats_per_cpu_storage = nullptr; 33 static size_t g_num_cores; 34 35 void grpc_stats_init(void) { 36 g_num_cores = GPR_MAX(1, gpr_cpu_num_cores()); 37 grpc_stats_per_cpu_storage = static_cast<grpc_stats_data*>( 38 gpr_zalloc(sizeof(grpc_stats_data) * g_num_cores)); 39 } 40 41 void grpc_stats_shutdown(void) { gpr_free(grpc_stats_per_cpu_storage); } 42 43 void grpc_stats_collect(grpc_stats_data* output) { 44 memset(output, 0, sizeof(*output)); 45 for (size_t core = 0; core < g_num_cores; core++) { 46 for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { 47 output->counters[i] += gpr_atm_no_barrier_load( 48 &grpc_stats_per_cpu_storage[core].counters[i]); 49 } 50 for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { 51 output->histograms[i] += gpr_atm_no_barrier_load( 52 &grpc_stats_per_cpu_storage[core].histograms[i]); 53 } 54 } 55 } 56 57 void grpc_stats_diff(const grpc_stats_data* b, const grpc_stats_data* a, 58 grpc_stats_data* c) { 59 for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { 60 c->counters[i] = b->counters[i] - a->counters[i]; 61 } 62 for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { 63 c->histograms[i] = b->histograms[i] - a->histograms[i]; 64 } 65 } 66 67 int grpc_stats_histo_find_bucket_slow(int value, const int* table, 68 int table_size) { 69 GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS(); 70 const int* const start = table; 71 while (table_size > 0) { 72 int step = table_size / 2; 73 const int* it = table + step; 74 if (value >= *it) { 75 table = it + 1; 76 table_size -= step + 1; 77 } else { 78 table_size = step; 79 } 80 } 81 return static_cast<int>(table - start) - 1; 82 } 83 84 size_t grpc_stats_histo_count(const grpc_stats_data* stats, 85 grpc_stats_histograms histogram) { 86 size_t sum = 0; 87 for (int i = 0; i < grpc_stats_histo_buckets[histogram]; i++) { 88 sum += static_cast<size_t>( 89 stats->histograms[grpc_stats_histo_start[histogram] + i]); 90 } 91 return sum; 92 } 93 94 static double threshold_for_count_below(const gpr_atm* bucket_counts, 95 const int* bucket_boundaries, 96 int num_buckets, double count_below) { 97 double count_so_far; 98 double lower_bound; 99 double upper_bound; 100 int lower_idx; 101 int upper_idx; 102 103 /* find the lowest bucket that gets us above count_below */ 104 count_so_far = 0.0; 105 for (lower_idx = 0; lower_idx < num_buckets; lower_idx++) { 106 count_so_far += static_cast<double>(bucket_counts[lower_idx]); 107 if (count_so_far >= count_below) { 108 break; 109 } 110 } 111 if (count_so_far == count_below) { 112 /* this bucket hits the threshold exactly... we should be midway through 113 any run of zero values following the bucket */ 114 for (upper_idx = lower_idx + 1; upper_idx < num_buckets; upper_idx++) { 115 if (bucket_counts[upper_idx]) { 116 break; 117 } 118 } 119 return (bucket_boundaries[lower_idx] + bucket_boundaries[upper_idx]) / 2.0; 120 } else { 121 /* treat values as uniform throughout the bucket, and find where this value 122 should lie */ 123 lower_bound = bucket_boundaries[lower_idx]; 124 upper_bound = bucket_boundaries[lower_idx + 1]; 125 return upper_bound - (upper_bound - lower_bound) * 126 (count_so_far - count_below) / 127 static_cast<double>(bucket_counts[lower_idx]); 128 } 129 } 130 131 double grpc_stats_histo_percentile(const grpc_stats_data* stats, 132 grpc_stats_histograms histogram, 133 double percentile) { 134 size_t count = grpc_stats_histo_count(stats, histogram); 135 if (count == 0) return 0.0; 136 return threshold_for_count_below( 137 stats->histograms + grpc_stats_histo_start[histogram], 138 grpc_stats_histo_bucket_boundaries[histogram], 139 grpc_stats_histo_buckets[histogram], 140 static_cast<double>(count) * percentile / 100.0); 141 } 142 143 char* grpc_stats_data_as_json(const grpc_stats_data* data) { 144 gpr_strvec v; 145 char* tmp; 146 bool is_first = true; 147 gpr_strvec_init(&v); 148 gpr_strvec_add(&v, gpr_strdup("{")); 149 for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { 150 gpr_asprintf(&tmp, "%s\"%s\": %" PRIdPTR, is_first ? "" : ", ", 151 grpc_stats_counter_name[i], data->counters[i]); 152 gpr_strvec_add(&v, tmp); 153 is_first = false; 154 } 155 for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) { 156 gpr_asprintf(&tmp, "%s\"%s\": [", is_first ? "" : ", ", 157 grpc_stats_histogram_name[i]); 158 gpr_strvec_add(&v, tmp); 159 for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) { 160 gpr_asprintf(&tmp, "%s%" PRIdPTR, j == 0 ? "" : ",", 161 data->histograms[grpc_stats_histo_start[i] + j]); 162 gpr_strvec_add(&v, tmp); 163 } 164 gpr_asprintf(&tmp, "], \"%s_bkt\": [", grpc_stats_histogram_name[i]); 165 gpr_strvec_add(&v, tmp); 166 for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) { 167 gpr_asprintf(&tmp, "%s%d", j == 0 ? "" : ",", 168 grpc_stats_histo_bucket_boundaries[i][j]); 169 gpr_strvec_add(&v, tmp); 170 } 171 gpr_strvec_add(&v, gpr_strdup("]")); 172 is_first = false; 173 } 174 gpr_strvec_add(&v, gpr_strdup("}")); 175 tmp = gpr_strvec_flatten(&v, nullptr); 176 gpr_strvec_destroy(&v); 177 return tmp; 178 } 179