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
232 lines
5.0 KiB
|
2 weeks ago
|
#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;
|
||
|
|
}
|