commit d4b13c69f651e20742a06cfceeeb622e63d2b7e7 Author: Denis Tereshkin Date: Wed Nov 19 21:43:29 2025 +0700 Initial implementation diff --git a/include/eventlog.h b/include/eventlog.h new file mode 100644 index 0000000..ae210f0 --- /dev/null +++ b/include/eventlog.h @@ -0,0 +1,46 @@ +#ifndef _EVENTLOG_H_ +#define _EVENTLOG_H_ + +#include +#include +#include + +#define EVENTLOG_DATA_MAX_SIZE (128 - 16) + +#define EVENTLOG_TYPE_STRING 0 + + +struct eventlog_entry +{ + atomic_uint_fast64_t timestamp; + uint32_t type_id; + uint32_t datalen; + uint8_t data[EVENTLOG_DATA_MAX_SIZE]; +}; + +struct eventlog_shm_header +{ + uint32_t magic; + uint32_t version; + atomic_uint_fast64_t tail; + uint64_t size; + struct eventlog_entry* entries; +}; + +enum eventlog_mode +{ + EVENTLOG_MODE_CREATE = 0, + EVENTLOG_MODE_ATTACH = 1, +}; + +int eventlog_init(const char* path, enum eventlog_mode mode); +int eventlog_shutdown(); + +int eventlog_emit_event(uint64_t timestamp, const char* fmt, ...); +int eventlog_emit_event_struct(uint64_t timestamp, uint32_t type, void* data, size_t data_size); +uint64_t eventlog_get_timestamp(); + +int eventlog_get_snapshot(uint64_t* head, struct eventlog_entry* buffer, size_t buffer_size, size_t* actual_size); + +#endif /* _EVENTLOG_H_ */ + diff --git a/src/eventlog.c b/src/eventlog.c new file mode 100644 index 0000000..0c47be5 --- /dev/null +++ b/src/eventlog.c @@ -0,0 +1,231 @@ +#include "eventlog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}