blob: 5baea34d6f66b8865bb40c239dc4a5daf6b3c79f [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 "stats.h"
#include <chrono>
#include <mutex>
#include "fmt/format.h"
#include "time_util.h"
Stats::Stats(std::vector<double> bucket_boundaries) : bucket_boundaries(std::move(bucket_boundaries)) {
for (int i = 0; i < STATS_METRIC_COUNT; i++) {
InstMetric im;
im.last_sample_time_ms = 0;
im.last_sample_count = 0;
im.idx = 0;
for (uint64_t &sample : im.samples) {
sample = 0;
}
inst_metrics.push_back(im);
}
}
#if defined(__APPLE__)
#include <mach/mach_init.h>
#include <mach/task.h>
int64_t Stats::GetMemoryRSS() {
task_t task = MACH_PORT_NULL;
task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS) return 0;
task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
return static_cast<int64_t>(t_info.resident_size);
}
#else
#include <fcntl.h>
#include <cstring>
#include <string>
#include "unique_fd.h"
int64_t Stats::GetMemoryRSS() {
char buf[4096];
auto fd = UniqueFD(open(fmt::format("/proc/{}/stat", getpid()).c_str(), O_RDONLY));
if (!fd) return 0;
if (read(*fd, buf, sizeof(buf)) <= 0) {
return 0;
}
fd.Close();
char *start = buf;
int count = 23; // RSS is the 24th field in /proc/<pid>/stat
while (start && count--) {
start = strchr(start, ' ');
if (start) start++;
}
if (!start) return 0;
char *stop = strchr(start, ' ');
if (!stop) return 0;
*stop = '\0';
int rss = std::atoi(start);
return static_cast<int64_t>(rss * sysconf(_SC_PAGESIZE));
}
#endif
void Stats::IncrCalls(const std::string &command_name) {
total_calls.fetch_add(1, std::memory_order_relaxed);
commands_stats[command_name].calls.fetch_add(1, std::memory_order_relaxed);
if (bucket_boundaries.size() > 0) {
commands_histogram[command_name].calls.fetch_add(1, std::memory_order_relaxed);
}
}
void Stats::IncrLatency(uint64_t latency, const std::string &command_name) {
commands_stats[command_name].latency.fetch_add(latency, std::memory_order_relaxed);
if (bucket_boundaries.size() > 0) {
commands_histogram[command_name].sum.fetch_add(latency, std::memory_order_relaxed);
const auto bucket_index = static_cast<std::size_t>(std::distance(
bucket_boundaries.begin(), std::lower_bound(bucket_boundaries.begin(), bucket_boundaries.end(), latency)));
commands_histogram[command_name].buckets[bucket_index]->fetch_add(1, std::memory_order_relaxed);
}
}
void Stats::TrackInstantaneousMetric(int metric, uint64_t current_reading) {
uint64_t curr_time_ms = util::GetTimeStampMS();
std::unique_lock<std::shared_mutex> lock(inst_metrics_mutex);
uint64_t t = curr_time_ms - inst_metrics[metric].last_sample_time_ms;
uint64_t ops = current_reading - inst_metrics[metric].last_sample_count;
uint64_t ops_sec = t > 0 ? (ops * 1000 / t) : 0;
inst_metrics[metric].samples[inst_metrics[metric].idx] = ops_sec;
inst_metrics[metric].idx++;
inst_metrics[metric].idx %= STATS_METRIC_SAMPLES;
inst_metrics[metric].last_sample_time_ms = curr_time_ms;
inst_metrics[metric].last_sample_count = current_reading;
}
uint64_t Stats::GetInstantaneousMetric(int metric) const {
std::shared_lock<std::shared_mutex> lock(inst_metrics_mutex);
uint64_t sum = 0;
for (uint64_t sample : inst_metrics[metric].samples) sum += sample;
return sum / STATS_METRIC_SAMPLES;
}