You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
5.0 KiB

#include "eventlog.h"
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdatomic.h>
#define DEFAULT_MAX_ENTRIES 131072
#define EVENTLOG_MAGIC 0x12345678
static int gs_fd;
static struct eventlog_shm_header* gs_header;
static struct eventlog_entry* gs_entries;
static int eventlog_create(const char* path, const size_t entries_num)
{
gs_fd = shm_open(path, O_CREAT | O_RDWR, 0666);
if (gs_fd < 0)
{
return -1;
}
const size_t expected_size = entries_num * sizeof(struct eventlog_entry) +
sizeof(struct eventlog_shm_header);
if(ftruncate(gs_fd, expected_size) < 0)
{
close(gs_fd);
return -2;
}
void* const addr = mmap(NULL, expected_size, PROT_READ | PROT_WRITE, MAP_SHARED, gs_fd, 0);
close(gs_fd);
if(addr == MAP_FAILED)
{
return -3;
}
gs_header = addr;
gs_header->magic = EVENTLOG_MAGIC;
gs_header->version = 1;
gs_header->size = entries_num;
gs_entries = (struct eventlog_entry*)(gs_header + 1);
atomic_store(&gs_header->tail, 0);
return 0;
}
static int eventlog_attach(const char* path)
{
gs_fd = shm_open(path, O_CREAT | O_RDWR, 0666);
if (gs_fd < 0)
{
return -1;
}
const off_t size = lseek(gs_fd, 0, SEEK_END);
if (size == -1) {
close(gs_fd);
return -1;
}
void* const addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, gs_fd, 0);
close(gs_fd);
if(addr == MAP_FAILED)
{
return -1;
}
gs_header = addr;
if (gs_header->magic != EVENTLOG_MAGIC)
{
munmap(addr, size);
return -1;
}
if (gs_header->version != 1)
{
munmap(addr, size);
return -1;
}
gs_entries = (struct eventlog_entry*)(gs_header + 1);
return 0;
}
int eventlog_init(const char* path, enum eventlog_mode mode)
{
if (gs_header != NULL)
{
return -1;
}
if (mode == EVENTLOG_MODE_CREATE)
{
return eventlog_create(path, DEFAULT_MAX_ENTRIES);
}
else
{
return eventlog_attach(path);
}
}
int eventlog_shutdown()
{
// TODO
return 0;
}
int eventlog_emit_event(uint64_t timestamp, const char* fmt, ...)
{
va_list args;
char data[EVENTLOG_DATA_MAX_SIZE];
va_start(args, fmt);
const int chars = vsnprintf(data, sizeof(data), fmt, args);
va_end(args);
if (chars < 0 || chars >= sizeof(data))
{
return -1;
}
return eventlog_emit_event_struct(timestamp, EVENTLOG_TYPE_STRING, data, chars + 1);
}
int eventlog_emit_event_struct(uint64_t timestamp, uint32_t type, void* data, size_t data_size)
{
uint64_t tail = 0;
uint64_t new_tail = 0;
if (data_size > EVENTLOG_DATA_MAX_SIZE)
{
return -1;
}
do
{
tail = atomic_load_explicit(&gs_header->tail, memory_order_relaxed);
new_tail = tail + 1;
if (new_tail >= gs_header->size)
{
new_tail = 0;
}
}
while(!atomic_compare_exchange_weak(&gs_header->tail, &tail, new_tail));
memcpy(&gs_entries[tail].data, data, data_size);
gs_entries[tail].datalen = data_size;
gs_entries[tail].type_id = type;
atomic_thread_fence(memory_order_release);
atomic_store_explicit(&gs_entries[tail].timestamp, timestamp, memory_order_release);
return 0;
}
uint64_t eventlog_get_timestamp()
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
}
int eventlog_get_snapshot(uint64_t* head, struct eventlog_entry* buffer, size_t buffer_size, size_t* actual_size)
{
const uint64_t tail = atomic_load(&gs_header->tail);
if (tail == *head)
{
*actual_size = 0;
}
else if (tail > *head)
{
size_t tocopy = tail - *head;
if (tocopy > buffer_size)
{
tocopy = buffer_size;
}
memcpy(buffer, &gs_entries[*head], tocopy * sizeof(struct eventlog_entry));
*head += tocopy;
*actual_size = tocopy;
}
else // tail < *head
{
size_t total_left = gs_header->size - (*head - tail) - 1;
size_t part1 = gs_header->size - *head;
size_t part2 = tail;
if (total_left > buffer_size)
{
total_left = buffer_size;
}
if (part1 > buffer_size)
{
part1 = buffer_size;
}
memcpy(buffer, &gs_entries[*head], part1 * sizeof(struct eventlog_entry));
*head += part1;
total_left -= part1;
if (part1 == buffer_size)
{
*actual_size = part1;
}
else
{
if (*head == gs_header->size)
{
*head = 0;
}
if (part2 > total_left)
{
part2 = total_left;
}
memcpy(buffer + part1, gs_entries, part2);
*head += part2;
*actual_size = part1 + part2;
}
}
return 0;
}