#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; }