• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <vector>
27 
28 #include "absl/strings/str_format.h"
29 #include "absl/strings/str_join.h"
30 
31 #include <grpc/support/alloc.h>
32 #include <grpc/support/string_util.h>
33 
34 #include "src/core/lib/gpr/string.h"
35 #include "src/core/lib/gpr/useful.h"
36 
37 grpc_stats_data* grpc_stats_per_cpu_storage = nullptr;
38 static size_t g_num_cores;
39 
grpc_stats_init(void)40 void grpc_stats_init(void) {
41   g_num_cores = GPR_MAX(1, gpr_cpu_num_cores());
42   grpc_stats_per_cpu_storage = static_cast<grpc_stats_data*>(
43       gpr_zalloc(sizeof(grpc_stats_data) * g_num_cores));
44 }
45 
grpc_stats_shutdown(void)46 void grpc_stats_shutdown(void) { gpr_free(grpc_stats_per_cpu_storage); }
47 
grpc_stats_collect(grpc_stats_data * output)48 void grpc_stats_collect(grpc_stats_data* output) {
49   memset(output, 0, sizeof(*output));
50   for (size_t core = 0; core < g_num_cores; core++) {
51     for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) {
52       output->counters[i] += gpr_atm_no_barrier_load(
53           &grpc_stats_per_cpu_storage[core].counters[i]);
54     }
55     for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) {
56       output->histograms[i] += gpr_atm_no_barrier_load(
57           &grpc_stats_per_cpu_storage[core].histograms[i]);
58     }
59   }
60 }
61 
grpc_stats_diff(const grpc_stats_data * b,const grpc_stats_data * a,grpc_stats_data * c)62 void grpc_stats_diff(const grpc_stats_data* b, const grpc_stats_data* a,
63                      grpc_stats_data* c) {
64   for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) {
65     c->counters[i] = b->counters[i] - a->counters[i];
66   }
67   for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) {
68     c->histograms[i] = b->histograms[i] - a->histograms[i];
69   }
70 }
71 
grpc_stats_histo_find_bucket_slow(int value,const int * table,int table_size)72 int grpc_stats_histo_find_bucket_slow(int value, const int* table,
73                                       int table_size) {
74   GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS();
75   const int* const start = table;
76   while (table_size > 0) {
77     int step = table_size / 2;
78     const int* it = table + step;
79     if (value >= *it) {
80       table = it + 1;
81       table_size -= step + 1;
82     } else {
83       table_size = step;
84     }
85   }
86   return static_cast<int>(table - start) - 1;
87 }
88 
grpc_stats_histo_count(const grpc_stats_data * stats,grpc_stats_histograms histogram)89 size_t grpc_stats_histo_count(const grpc_stats_data* stats,
90                               grpc_stats_histograms histogram) {
91   size_t sum = 0;
92   for (int i = 0; i < grpc_stats_histo_buckets[histogram]; i++) {
93     sum += static_cast<size_t>(
94         stats->histograms[grpc_stats_histo_start[histogram] + i]);
95   }
96   return sum;
97 }
98 
threshold_for_count_below(const gpr_atm * bucket_counts,const int * bucket_boundaries,int num_buckets,double count_below)99 static double threshold_for_count_below(const gpr_atm* bucket_counts,
100                                         const int* bucket_boundaries,
101                                         int num_buckets, double count_below) {
102   double count_so_far;
103   double lower_bound;
104   double upper_bound;
105   int lower_idx;
106   int upper_idx;
107 
108   /* find the lowest bucket that gets us above count_below */
109   count_so_far = 0.0;
110   for (lower_idx = 0; lower_idx < num_buckets; lower_idx++) {
111     count_so_far += static_cast<double>(bucket_counts[lower_idx]);
112     if (count_so_far >= count_below) {
113       break;
114     }
115   }
116   if (count_so_far == count_below) {
117     /* this bucket hits the threshold exactly... we should be midway through
118        any run of zero values following the bucket */
119     for (upper_idx = lower_idx + 1; upper_idx < num_buckets; upper_idx++) {
120       if (bucket_counts[upper_idx]) {
121         break;
122       }
123     }
124     return (bucket_boundaries[lower_idx] + bucket_boundaries[upper_idx]) / 2.0;
125   } else {
126     /* treat values as uniform throughout the bucket, and find where this value
127        should lie */
128     lower_bound = bucket_boundaries[lower_idx];
129     upper_bound = bucket_boundaries[lower_idx + 1];
130     return upper_bound - (upper_bound - lower_bound) *
131                              (count_so_far - count_below) /
132                              static_cast<double>(bucket_counts[lower_idx]);
133   }
134 }
135 
grpc_stats_histo_percentile(const grpc_stats_data * stats,grpc_stats_histograms histogram,double percentile)136 double grpc_stats_histo_percentile(const grpc_stats_data* stats,
137                                    grpc_stats_histograms histogram,
138                                    double percentile) {
139   size_t count = grpc_stats_histo_count(stats, histogram);
140   if (count == 0) return 0.0;
141   return threshold_for_count_below(
142       stats->histograms + grpc_stats_histo_start[histogram],
143       grpc_stats_histo_bucket_boundaries[histogram],
144       grpc_stats_histo_buckets[histogram],
145       static_cast<double>(count) * percentile / 100.0);
146 }
147 
grpc_stats_data_as_json(const grpc_stats_data * data)148 std::string grpc_stats_data_as_json(const grpc_stats_data* data) {
149   std::vector<std::string> parts;
150   parts.push_back("{");
151   for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) {
152     parts.push_back(absl::StrFormat(
153         "\"%s\": %" PRIdPTR, grpc_stats_counter_name[i], data->counters[i]));
154   }
155   for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) {
156     parts.push_back(absl::StrFormat("\"%s\": [", grpc_stats_histogram_name[i]));
157     for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) {
158       parts.push_back(
159           absl::StrFormat("%s%" PRIdPTR, j == 0 ? "" : ",",
160                           data->histograms[grpc_stats_histo_start[i] + j]));
161     }
162     parts.push_back(
163         absl::StrFormat("], \"%s_bkt\": [", grpc_stats_histogram_name[i]));
164     for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) {
165       parts.push_back(absl::StrFormat(
166           "%s%d", j == 0 ? "" : ",", grpc_stats_histo_bucket_boundaries[i][j]));
167     }
168     parts.push_back("]");
169   }
170   parts.push_back("}");
171   return absl::StrJoin(parts, "");
172 }
173