Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 114 additions & 8 deletions src/kernel/logging/klog.c
Original file line number Diff line number Diff line change
@@ -1,14 +1,97 @@
#include "klog.h"

#include <stdarg.h>
#include <stdbigos/stdio.h>
#include <stdbigos/types.h>

// NOTE: In the future this lib should manage logging (formating and outputing to the screen or uart) on its own.
// Right now I will leave it as is, will change once the basic kernel at least runs.
#include "debug/debug_stdio.h"

static u32 g_indent_level = 0;
static const char* g_prefixes[] = {"[ERROR]", "[WARNING]", "[ ]", "[~]"};
static const char* g_overflow_warning = "[WARNING]Some of the logs were lost due to overflow";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a space after the loglvl prefix.


#ifdef __DEBUG__
#define RING_BUF_SIZE (1 << 20)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no point of using macros, just use const size_t RING_BUF_SIZE = ...;

#else
#define RING_BUF_SIZE (1 << 16)
#endif // !__DEBUG__

#define TEMP_BUF_SIZE (1 << 12)

typedef struct {
char ring[RING_BUF_SIZE];
i32 read;
i32 write;
bool full;
bool overflown;
void (*uart_tx)(char c);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be a part of the ring buffer structure.

} ring_buffer_t;

static ring_buffer_t g_buffer;

static ring_buffer_t* get_buffer() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function necessary?

return &g_buffer;
}

static void put_char(ring_buffer_t* rb, const char c) {
if (c == '\0')
return; // Null characters are not suppossed to be logged.
rb->ring[rb->write++] = c;
rb->write %= RING_BUF_SIZE;
if (rb->full) {
rb->read = (rb->read + 1) % RING_BUF_SIZE;
rb->overflown = true;
}
rb->full = rb->read == rb->write;
if (rb->uart_tx)
rb->uart_tx(c);
}

static void put_string(ring_buffer_t* rb, const char* str) {
size_t it = 0;
while (str[it]) put_char(rb, str[it++]);
}

static void put_overflow_warning(ring_buffer_t* rb) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no point in copying this message to the ring buffer since it is stored already in static memory.
When serial or tty is being initialized simply check if ring buffer has overflown and display this message before dumping the ring buffer contents.

size_t it = 0;
i32 temp_write = rb->write;
while (g_overflow_warning[it]) {
rb->ring[temp_write++] = g_overflow_warning[it++];
temp_write %= RING_BUF_SIZE;
}
}

static void put_msg(ring_buffer_t* rb, const char* msg) {
put_string(rb, msg);
if (rb->overflown)
put_overflow_warning(rb);
}

char ring_buffer_get_char() {
ring_buffer_t* rb = get_buffer();
if (rb->read == rb->write && !rb->full)
return 0;
const char ret = rb->ring[rb->read++];
rb->read %= RING_BUF_SIZE;
rb->full = false;
rb->overflown = false;
return ret;
}

void ring_buffer_flush_to_uart() {
ring_buffer_t* rb = get_buffer();
if (!rb->uart_tx)
return;
char c;
while ((c = ring_buffer_get_char())) rb->uart_tx(c);
}

void ring_buffer_set_uart_tx_function(void (*uart_tx)(char c), bool flush) {
ring_buffer_t* rb = get_buffer();
rb->uart_tx = uart_tx;
if (flush)
ring_buffer_flush_to_uart();
}

void klog_indent_increase() {
++g_indent_level;
Expand All @@ -20,22 +103,45 @@ void klog_indent_decrease() {
}

static void klogv(klog_severity_level_t loglvl, const char* fmt, va_list va) {
(void)fmt;
(void)va;
char temp_buffer[TEMP_BUF_SIZE];

if (loglvl > KLSL_TRACE) {
KLOGLN_ERROR("Invalid loglvl passed to klog");
loglvl = KLSL_ERROR;
}

DEBUG_PUTGAP(g_indent_level);
DEBUG_PRINTF("%s ", g_prefixes[loglvl]);
DEBUG_VPRINTF(fmt, va);
size_t written = 0;
for (u32 i = 0; i < g_indent_level && written < TEMP_BUF_SIZE - 1; i++) temp_buffer[written++] = ' ';
Copy link
Copy Markdown
Contributor

@frogrammer9 frogrammer9 Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing '\t' chars in the ring buffer can be potentially wasteful. ASCII values between 0 and 8 are not and will be not important for logger so we can store those values and when printing interpret then as the size of the indentation.


const char* prefix = g_prefixes[loglvl];
for (size_t i = 0; prefix[i] && written < TEMP_BUF_SIZE - 1; i++) temp_buffer[written++] = prefix[i];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a space after the loglvl prefix.


va_list copy;
va_copy(copy, va);
int n = vsnprintf(temp_buffer + written, TEMP_BUF_SIZE - written, fmt, copy);
va_end(copy);

// stbsp_vsnprintf doesn't return errors, so just check for truncation
if (n < 0)
n = 0;
written += (size_t)n;

if (written >= TEMP_BUF_SIZE)
written = TEMP_BUF_SIZE - 1;
temp_buffer[written] = '\0';

ring_buffer_t* rb = get_buffer();
put_msg(rb, temp_buffer);

// DEBUG_PUTGAP(g_indent_level);
// DEBUG_PRINTF("%s ", g_prefixes[loglvl]);
// DEBUG_VPRINTF(fmt, va);
}

static void kloglnv(klog_severity_level_t loglvl, const char* fmt, va_list va) {
klogv(loglvl, fmt, va);
DEBUG_PUTC('\n');
put_msg(&g_buffer, "\n");
// DEBUG_PUTC('\n');
}

void klog(klog_severity_level_t loglvl, const char* fmt, ...) {
Expand Down
3 changes: 3 additions & 0 deletions src/kernel/logging/klog.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ typedef enum {
KLSL_TRACE = 3,
} klog_severity_level_t;

char ring_buffer_get_char();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existence of the ring buffer is some unimportant implementation detail for the logger user, there is no point in providing this information. Those function should be named something like "klog_flush_serial" "klog_set_serial_handler", the functionality of getting chars from the buffer is unnecessary.

void ring_buffer_flush_to_uart();
void ring_buffer_set_uart_tx_function(void (*uart_tx)(char c), bool flush);
void klog_indent_increase();
void klog_indent_decrease();

Expand Down
Loading