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
grpc_stats_init(void)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
grpc_stats_shutdown(void)41 void grpc_stats_shutdown(void) { gpr_free(grpc_stats_per_cpu_storage); }
42
grpc_stats_collect(grpc_stats_data * output)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
grpc_stats_diff(const grpc_stats_data * b,const grpc_stats_data * a,grpc_stats_data * c)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
grpc_stats_histo_find_bucket_slow(int value,const int * table,int table_size)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
grpc_stats_histo_count(const grpc_stats_data * stats,grpc_stats_histograms histogram)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
threshold_for_count_below(const gpr_atm * bucket_counts,const int * bucket_boundaries,int num_buckets,double count_below)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
grpc_stats_histo_percentile(const grpc_stats_data * stats,grpc_stats_histograms histogram,double percentile)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
grpc_stats_data_as_json(const grpc_stats_data * data)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