Home | History | Annotate | Download | only in debug
      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