Okay, I think I solved the problem.
So basically, I changed the location of the pointer from the inside of the static function to the inline static variable in the class. And when I looked at where those variables are stored, I realized they were stored in the .gnu.linkonce sections. (Note for me in the future: to view the locations of all the objects in the kernel, you do objdump Kernel.elf -x and grep it however you like!)
So, I added this snipplet of code that relocates the groups of .gnu.linkonce sections at the rear of the kernel(more specifically, next to the .bss section.)
/* At kernel_linker.ld */
...
.bss ALIGN(4096) : {
__bss_start__ = .;
*(.bss) *(.rodata) *(.bss.*)
__bss_end__ = .;
}
.gnu.linkonce ALIGN(4096) : {
__gnu_linkonce_start__ = .;
*(.gnu.linkonce.*)
__gnu_linkonce_end__ = .;
}
}
By doing this, hopefully it would store those static pointers that points to the locations of singleton objects into more reliable and predictable space that would be significantly less prone to whatever errors I was dealing with. And it actually did. That weird error just completely disappeared(I really hope it did and I'm not just hastily declaring it as fixed.)
Then I had a better idea of making a dedicated section that would store the pointers of singleton objects :
...
.bss ALIGN(4096) : {
__bss_start__ = .;
*(.bss) *(.rodata) *(.bss.*)
__bss_end__ = .;
}
.singleton_obj_ptrs ALIGN(4096) : {
__singleton_obj_ptrs_start__ = .;
*(.singleton_obj_ptrs) *(.singleton_obj_ptrs.*)
__singleton_obj_ptrs_end__ = .;
}
.gnu.linkonce ALIGN(4096) : {
__gnu_linkonce_start__ = .;
*(.gnu.linkonce.*)
__gnu_linkonce_end__ = .;
}
}
And just like what I did for the main function and driver init functions, I will have a macro for making a variable stored in the section :
#define __singleton_ptr__ __attribute__ ((section(".singleton_obj_ptrs")))
...And this is the latest version of singleton system we're using(for both kstruct and pmem) :
#define SINGLETON_FUNCTION get_self
#define SINGLETON_PATTERN_KSTRUCT(type) \
inline static __singleton_ptr__ type *__singleton_object = 0x00; \
static type *SINGLETON_FUNCTION(void) {\
if(__singleton_object == 0x00) { \
__singleton_object = (type *)memory::kstruct_alloc(sizeof(type));\
}\
return __singleton_object;\
}
#define SINGLETON_PATTERN_PMEM(type) \
inline static __singleton_ptr__ type *__singleton_object = 0x00; \
static type *SINGLETON_FUNCTION(void) {\
if(__singleton_object == 0x00) { \
__singleton_object = (type *)memory::pmem_alloc(sizeof(type));\
} \
return __singleton_object;\
}
I think this fixed the issue, but if it's not, that's another job for future Ian.
Now that thing's cleared, I'm going to focus on adding some features in the debug system.
I come up with a way to filter out the logs without having to do any additional operations of filtering(excluding the adding the message to the logger). Basically you have arrays of linked lists, each corresponding to the levels of the log. The gist of it is that you store the message to the list of that certain log level. Say the level is 2, you store the message into 2nd linked list from the array. That would easily sort the debug message into level of importance in the first place! Well let's not to mention that we also have a single list that just stores all the message regardless of the log levels in the order of time arrived.
LinkedList<debug_message_t*>debug_list_log_lvl[DEBUG_MAX_LOG_LEVEL];
LinkedList<debug_message_t*>full_debug_list;
I haven't fully come up with the concrete structure of the debug_message_t, but I think it would include (most importantly) the message itself, level, time(maybe like based on CPU clock cycle), and other miscellaneous information that might be helpful to debugging the system.
This is an idea that I came up with right now: what if you store the caller stack for every debug::out::printf you call? Would that be really helpful in debugging the system, or would it just be a total mess and waste of resource? I think it might be latter. You only need the call stack tracing whenever you encountered an unrecoverable error(like exception.) Maybe I'll add a feature to dump the call stacks when the kernel encountered an exception.
Also, maybe the __FILE__ and __LINE__ macros in C++ might come in handy in debugging stuff. I'll also add that to the lists of things I'll put on debug_message_t!