/*
*
* 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 <grpc/support/port_platform.h>
#include "src/core/lib/debug/stats.h"
#include <inttypes.h>
#include <string.h>
#include <grpc/support/alloc.h>
#include <grpc/support/string_util.h>
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gpr/useful.h"
grpc_stats_data* grpc_stats_per_cpu_storage = nullptr;
static size_t g_num_cores;
void grpc_stats_init(void) {
g_num_cores = GPR_MAX(1, gpr_cpu_num_cores());
grpc_stats_per_cpu_storage = static_cast<grpc_stats_data*>(
gpr_zalloc(sizeof(grpc_stats_data) * g_num_cores));
}
void grpc_stats_shutdown(void) { gpr_free(grpc_stats_per_cpu_storage); }
void grpc_stats_collect(grpc_stats_data* output) {
memset(output, 0, sizeof(*output));
for (size_t core = 0; core < g_num_cores; core++) {
for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) {
output->counters[i] += gpr_atm_no_barrier_load(
&grpc_stats_per_cpu_storage[core].counters[i]);
}
for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) {
output->histograms[i] += gpr_atm_no_barrier_load(
&grpc_stats_per_cpu_storage[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];
}
}
int grpc_stats_histo_find_bucket_slow(int value, const int* table,
int table_size) {
GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS();
const int* const start = table;
while (table_size > 0) {
int step = table_size / 2;
const int* it = table + step;
if (value >= *it) {
table = it + 1;
table_size -= step + 1;
} else {
table_size = step;
}
}
return static_cast<int>(table - start) - 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<size_t>(
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<double>(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<double>(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<double>(count) * percentile / 100.0);
}
char* grpc_stats_data_as_json(const grpc_stats_data* data) {
gpr_strvec v;
char* tmp;
bool is_first = true;
gpr_strvec_init(&v);
gpr_strvec_add(&v, gpr_strdup("{"));
for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) {
gpr_asprintf(&tmp, "%s\"%s\": %" PRIdPTR, is_first ? "" : ", ",
grpc_stats_counter_name[i], data->counters[i]);
gpr_strvec_add(&v, tmp);
is_first = false;
}
for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) {
gpr_asprintf(&tmp, "%s\"%s\": [", is_first ? "" : ", ",
grpc_stats_histogram_name[i]);
gpr_strvec_add(&v, tmp);
for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) {
gpr_asprintf(&tmp, "%s%" PRIdPTR, j == 0 ? "" : ",",
data->histograms[grpc_stats_histo_start[i] + j]);
gpr_strvec_add(&v, tmp);
}
gpr_asprintf(&tmp, "], \"%s_bkt\": [", grpc_stats_histogram_name[i]);
gpr_strvec_add(&v, tmp);
for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) {
gpr_asprintf(&tmp, "%s%d", j == 0 ? "" : ",",
grpc_stats_histo_bucket_boundaries[i][j]);
gpr_strvec_add(&v, tmp);
}
gpr_strvec_add(&v, gpr_strdup("]"));
is_first = false;
}
gpr_strvec_add(&v, gpr_strdup("}"));
tmp = gpr_strvec_flatten(&v, nullptr);
gpr_strvec_destroy(&v);
return tmp;
}