sslbd/stats_file.cpp
#include "stats_file.hpp"
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
typedef struct {
msec_t last;
stats_t last_stats;
char* map;
} stats_file_ctx_t;
const int stats_len = stats_num * (20 + 1); // decimal 64bit unsigned can be 20 chars long
static bool stats_file_config(void*& ctx, char* value) {
// open or create
int fd = open(value, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); // PROT_WRITE needs O_RDWR ?
if (fd == -1) {
log_errno(error, "open(%s)", value);
return false;
}
// truncate to correct size in any case
if (ftruncate(fd, stats_len*2) == -1) {
log_errno(error, "ftruncate(%s)", value);
EINTR_RETRY(close(fd));
return false; // TODO: unlink, too?
}
// map fd to mem
void* map = mmap(NULL, stats_len*2, PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
log_errno(error, "mmap(%s)", value);
EINTR_RETRY(close(fd));
return false;
}
// reset to zeroes
memset(map, '\0', stats_len*2);
if (msync(map, stats_len*2, MS_SYNC) == -1) {
log_errno(error, "msync(%s)", value);
}
// done
EINTR_RETRY(close(fd)); // fd not needed anymore
log(info, "writing stats to %s", value);
stats_file_ctx_t* c = (stats_file_ctx_t*)malloc(sizeof(stats_file_ctx_t));
memcpy(&c->last_stats, &stats, sizeof(stats));
c->last = NOW_MSEC;
c->map = (char*)map;
ctx = c;
return true;
}
static void stats_file_unconfig(void*& ctx) {
if (ctx) {
if (((stats_file_ctx_t*)ctx)->map) {
if (munmap(((stats_file_ctx_t*)ctx)->map, stats_len*2) == -1) {
log_errno(error, "munmap()");
}
}
free(ctx);
ctx = NULL;
}
}
CONF_DEF(config) {
ConfigKey stats_file;
};
CONF_INIT(config) {
CONF_KEY_INIT(stats_file, true, false, stats_file_config, stats_file_unconfig);
}
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
/** formats all counters into mapped memory **/
static bool write_stats() {
stats_file_ctx_t* ctx = config? config->stats_file.ctx_as<stats_file_ctx_t>(): NULL;
if (!ctx || !ctx->map) return true; // but try on as we could run before config was switched
const uint64_t* s = (uint64_t*)&stats;
// write absolute values
char* p = ctx->map;
for (int i=0; i<stats_num; ++i) {
p += sprintf(p, "%" PRIu64 " ", s[i]);
}
p[-1] = '\n';
// compute rates
msec_t now = NOW_MSEC;
msec_t delta = now - ctx->last;
if (delta <= 0) delta = 1;
for (int i=0; i<stats_num; ++i) {
uint64_t d = 0;
if (s[i] > ((uint64_t*)&ctx->last_stats)[i]) { // ofl?
d = s[i] - ((uint64_t*)&ctx->last_stats)[i];
d *= 1000; // per second (here, as should be no ofl and leaves more precision)
d /= delta;
}
p += sprintf(p, "%" PRIu64 " ", d);
}
p[-1] = '\n';
// zero out rest
memset(p, ' ', (ctx->map + (stats_len*2)) - p);
// backup current values for next run
memcpy(&ctx->last_stats, &stats, sizeof(stats));
ctx->last = now;
// ensure it's written
#if 1
if (msync(((stats_file_ctx_t*)ctx)->map, stats_len*2, MS_SYNC) == -1) { // TODO: needed?
log_errno(error, "msync()");
}
#endif
return true;
}
HOOK(POST_CONFIG, HOOK_PRIO_MID) {
if (config && config->stats_file.ctx_as<char>()) {
Periodic::getInst()->add(&write_stats, 5);
} else if (Periodic::hasInst()) { // don't create it if POST_IO
Periodic::getInst()->del(&write_stats);
}
}