commit
d4b13c69f6
2 changed files with 277 additions and 0 deletions
@ -0,0 +1,46 @@ |
|||||||
|
#ifndef _EVENTLOG_H_ |
||||||
|
#define _EVENTLOG_H_ |
||||||
|
|
||||||
|
#include <stdatomic.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
#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_ */ |
||||||
|
|
||||||
@ -0,0 +1,231 @@ |
|||||||
|
#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; |
||||||
|
} |
||||||
Loading…
Reference in new issue